diff options
320 files changed, 12460 insertions, 4061 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 4c82506516..f5a95fa0cf 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -46,6 +46,9 @@ ifeq ($(ART_BUILD_HOST_DEBUG),false) $(info Disabling ART_BUILD_HOST_DEBUG) endif +# Enable the read barrier by default. +ART_USE_READ_BARRIER ?= true + ART_CPP_EXTENSION := .cc ifndef LIBART_IMG_HOST_BASE_ADDRESS diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 5bdfbc74eb..d8b780ac35 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -107,6 +107,7 @@ ART_GTEST_proxy_test_DEX_DEPS := Interfaces ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex +ART_GTEST_runtime_callbacks_test_DEX_DEPS := XandY ART_GTEST_stub_test_DEX_DEPS := AllFields ART_GTEST_transaction_test_DEX_DEPS := Transaction ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup @@ -120,14 +121,14 @@ ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_optimizing_no-pic_6 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(HOST_CORE_IMAGE_optimizing_pic_64) \ $(HOST_CORE_IMAGE_optimizing_pic_32) \ - $(HOST_CORE_IMAGE_optimizing_no-pic_64) \ - $(HOST_CORE_IMAGE_optimizing_no-pic_32) \ + $(HOST_CORE_IMAGE_interpreter_pic_64) \ + $(HOST_CORE_IMAGE_interpreter_pic_32) \ $(HOST_OUT_EXECUTABLES)/patchoatd ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_optimizing_pic_64) \ $(TARGET_CORE_IMAGE_optimizing_pic_32) \ - $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \ - $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \ + $(TARGET_CORE_IMAGE_interpreter_pic_64) \ + $(TARGET_CORE_IMAGE_interpreter_pic_32) \ $(TARGET_OUT_EXECUTABLES)/patchoatd ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ diff --git a/build/art.go b/build/art.go index e6e0544e4d..84269c3f23 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 envTrue(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 e41d9bde59..28c009edf0 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -770,8 +770,6 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> { existing = existing | ExperimentalFlags::kAgents; } else if (option == "runtime-plugins") { existing = existing | ExperimentalFlags::kRuntimePlugins; - } else if (option == "method-handles") { - existing = existing | ExperimentalFlags::kMethodHandles; } else { return Result::Failure(std::string("Unknown option '") + option + "'"); } diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index f4838c1119..0d45a50053 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -23,7 +23,7 @@ #include "common_runtime_test.h" #include "compiler.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "oat_file.h" namespace art { diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index bbf9eee0e5..e2a0942724 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -176,6 +176,7 @@ class LinkerPatch { kCallRelative, // NOTE: Actual patching is instruction_set-dependent. kType, kTypeRelative, // NOTE: Actual patching is instruction_set-dependent. + kTypeBssEntry, // NOTE: Actual patching is instruction_set-dependent. kString, kStringRelative, // NOTE: Actual patching is instruction_set-dependent. kStringBssEntry, // NOTE: Actual patching is instruction_set-dependent. @@ -228,6 +229,16 @@ class LinkerPatch { return patch; } + static LinkerPatch TypeBssEntryPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_type_idx) { + LinkerPatch patch(literal_offset, Type::kTypeBssEntry, target_dex_file); + patch.type_idx_ = target_type_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + static LinkerPatch StringPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_string_idx) { @@ -282,6 +293,7 @@ class LinkerPatch { switch (GetType()) { case Type::kCallRelative: case Type::kTypeRelative: + case Type::kTypeBssEntry: case Type::kStringRelative: case Type::kStringBssEntry: case Type::kDexCacheArray: @@ -299,12 +311,16 @@ class LinkerPatch { } const DexFile* TargetTypeDexFile() const { - DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative); + DCHECK(patch_type_ == Type::kType || + patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeBssEntry); return target_dex_file_; } dex::TypeIndex TargetTypeIndex() const { - DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative); + DCHECK(patch_type_ == Type::kType || + patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeBssEntry); return dex::TypeIndex(type_idx_); } @@ -334,6 +350,7 @@ class LinkerPatch { uint32_t PcInsnOffset() const { DCHECK(patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeBssEntry || patch_type_ == Type::kStringRelative || patch_type_ == Type::kStringBssEntry || patch_type_ == Type::kDexCacheArray); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 29502666ad..faf8b41be1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1060,13 +1060,13 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (auto& m : c->GetMethods(pointer_size)) { - ResolveExceptionsForMethod(&m, pointer_size); + ResolveExceptionsForMethod(&m); } return true; } private: - void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size) + void ResolveExceptionsForMethod(ArtMethod* method_handle) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile::CodeItem* code_item = method_handle->GetCodeItem(); if (code_item == nullptr) { @@ -1088,8 +1088,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { dex::TypeIndex encoded_catch_handler_handlers_type_idx = dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list)); // Add to set of types to resolve if not already in the dex cache resolved types - if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx, - pointer_size)) { + if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx, method_handle->GetDexFile()); } @@ -1950,66 +1949,82 @@ static void PopulateVerifiedMethods(const DexFile& dex_file, DCHECK(!it.HasNext()); } -void CompilerDriver::Verify(jobject jclass_loader, - const std::vector<const DexFile*>& dex_files, - TimingLogger* timings) { +bool CompilerDriver::FastVerify(jobject jclass_loader, + const std::vector<const DexFile*>& dex_files, + TimingLogger* timings) { verifier::VerifierDeps* verifier_deps = Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); // If there is an existing `VerifierDeps`, try to use it for fast verification. - if (verifier_deps != nullptr) { - TimingLogger::ScopedTiming t("Fast Verify", timings); - 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(); - if (verifier_deps->ValidateDependencies(class_loader, soa.Self())) { - // 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 - // time. So instead we assume these classes still need to be verified at - // runtime. - for (const DexFile* dex_file : dex_files) { - // Fetch the list of unverified classes and turn it into a set for faster - // lookups. - const std::vector<dex::TypeIndex>& unverified_classes = - verifier_deps->GetUnverifiedClasses(*dex_file); - std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); - 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()) { - // 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 - // the class. - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); - if (cls.Get() != nullptr) { - ObjectLock<mirror::Class> lock(soa.Self(), cls); - mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self()); - } else { - DCHECK(soa.Self()->IsExceptionPending()); - soa.Self()->ClearException(); - } - // Create `VerifiedMethod`s for each methods, the compiler expects one for - // quickening or compiling. - // Note that this means: - // - We're only going to compile methods that did verify. - // - Quickening will not do checkcast ellision. - // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + if (verifier_deps == nullptr) { + return false; + } + TimingLogger::ScopedTiming t("Fast Verify", timings); + 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(); + if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) { + return false; + } + + // 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 + // time. So instead we assume these classes still need to be verified at + // runtime. + for (const DexFile* dex_file : dex_files) { + // Fetch the list of unverified classes and turn it into a set for faster + // lookups. + const std::vector<dex::TypeIndex>& unverified_classes = + verifier_deps->GetUnverifiedClasses(*dex_file); + std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); + 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()) { + // 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 + // 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(); } + // Create `VerifiedMethod`s for each methods, the compiler expects one for + // quickening or compiling. + // Note that this means: + // - We're only going to compile methods that did verify. + // - Quickening will not do checkcast ellision. + // TODO(ngeoffray): Reconsider this once we refactor compiler filters. + PopulateVerifiedMethods(*dex_file, i, verification_results_); } } - return; } } + return true; +} + +void CompilerDriver::Verify(jobject jclass_loader, + const std::vector<const DexFile*>& dex_files, + TimingLogger* timings) { + if (FastVerify(jclass_loader, dex_files, timings)) { + return; + } // If there is no existing `verifier_deps` (because of non-existing vdex), or // the existing `verifier_deps` is not valid anymore, create a new one for @@ -2017,7 +2032,7 @@ void CompilerDriver::Verify(jobject jclass_loader, // Then dex2oat can update the vdex file with these new dependencies. if (!GetCompilerOptions().IsBootImage()) { // Create the main VerifierDeps, and set it to this thread. - verifier_deps = new verifier::VerifierDeps(dex_files); + verifier::VerifierDeps* verifier_deps = new verifier::VerifierDeps(dex_files); Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps); Thread::Current()->SetVerifierDeps(verifier_deps); // Create per-thread VerifierDeps to avoid contention on the main one. @@ -2026,6 +2041,7 @@ void CompilerDriver::Verify(jobject jclass_loader, worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files)); } } + // Note: verification should not be pulling in classes anymore when compiling the boot image, // as all should have been resolved before. As such, doing this in parallel should still // be deterministic. @@ -2041,6 +2057,7 @@ void CompilerDriver::Verify(jobject jclass_loader, if (!GetCompilerOptions().IsBootImage()) { // Merge all VerifierDeps into the main one. + verifier::VerifierDeps* verifier_deps = Thread::Current()->GetVerifierDeps(); for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps(); worker->GetThread()->SetVerifierDeps(nullptr); @@ -2061,7 +2078,10 @@ class VerifyClassVisitor : public CompilationVisitor { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *manager_->GetDexFile(); if (!manager_->GetCompiler()->ShouldVerifyClassBasedOnProfile(dex_file, class_def_index)) { - // Skip verification since the class is not in the profile. + // Skip verification since the class is not in the profile, and let the VerifierDeps know + // that the class will need to be verified at runtime. + verifier::VerifierDeps::MaybeRecordVerificationStatus( + dex_file, dex::TypeIndex(class_def_index), verifier::MethodVerifier::kSoftFailure); return; } const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 2e3b7c8eb5..503fe3adfc 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -33,7 +33,7 @@ #include "dex_file.h" #include "dex_file_types.h" #include "driver/compiled_method_storage.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "invoke_type.h" #include "method_reference.h" #include "mirror/class.h" // For mirror::Class::Status. @@ -433,12 +433,18 @@ class CompilerDriver { TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); + // Do fast verification through VerifierDeps if possible. Return whether + // verification was successful. // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a // single-threaded way. + bool FastVerify(jobject class_loader, + const std::vector<const DexFile*>& dex_files, + TimingLogger* timings) + NO_THREAD_SAFETY_ANALYSIS; + void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - TimingLogger* timings) - NO_THREAD_SAFETY_ANALYSIS; + TimingLogger* timings); void VerifyDexFile(jobject class_loader, const DexFile& dex_file, diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 12684c09c0..1e4ca16844 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -32,7 +32,7 @@ #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "handle_scope-inl.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 9c38445276..4109345f80 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -2346,8 +2346,6 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked())); ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_); copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_); - GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_); - copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_); // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to // oat_begin_ diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 4a9de7f3d1..79e1785e91 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -224,6 +224,7 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, } else { // LDR/STR 32-bit or 64-bit with imm12 == 0 (unset). DCHECK(patch.GetType() == LinkerPatch::Type::kDexCacheArray || + patch.GetType() == LinkerPatch::Type::kTypeBssEntry || patch.GetType() == LinkerPatch::Type::kStringBssEntry) << patch.GetType(); DCHECK_EQ(insn & 0xbfbffc00, 0xb9000000) << std::hex << insn; } diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 4180e0e6c9..34b33a13a3 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -487,7 +487,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(163 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(157 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index a9da09c82c..de5af97b9a 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -296,6 +296,7 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCo bss_start_(0u), bss_size_(0u), bss_roots_offset_(0u), + bss_type_entries_(), bss_string_entries_(), oat_data_offset_(0u), oat_header_(nullptr), @@ -585,7 +586,7 @@ void OatWriter::PrepareLayout(linker::MultiOatRelativePatcher* relative_patcher) } oat_size_ = offset; - if (!HasBootImage()) { + { TimingLogger::ScopedTiming split("InitBssLayout", timings_); InitBssLayout(instruction_set); } @@ -847,6 +848,10 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { if (!patch.IsPcRelative()) { writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset()); } + if (patch.GetType() == LinkerPatch::Type::kTypeBssEntry) { + TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); + writer_->bss_type_entries_.Overwrite(ref, /* placeholder */ 0u); + } if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) { StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u); @@ -1185,6 +1190,15 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { target_offset); break; } + case LinkerPatch::Type::kTypeBssEntry: { + TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); + uint32_t target_offset = writer_->bss_type_entries_.Get(ref); + writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, + patch, + offset_ + literal_offset, + target_offset); + break; + } case LinkerPatch::Type::kCall: { uint32_t target_offset = GetTargetOffset(patch); PatchCodeAddress(&patched_code_, literal_offset, target_offset); @@ -1619,20 +1633,34 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } void OatWriter::InitBssLayout(InstructionSet instruction_set) { - DCHECK(!HasBootImage()); + if (HasBootImage()) { + DCHECK(bss_string_entries_.empty()); + if (bss_type_entries_.empty()) { + // Nothing to put to the .bss section. + return; + } + } // Allocate space for app dex cache arrays in the .bss section. bss_start_ = RoundUp(oat_size_, kPageSize); - PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); bss_size_ = 0u; - for (const DexFile* dex_file : *dex_files_) { - dex_cache_arrays_offsets_.Put(dex_file, bss_start_ + bss_size_); - DexCacheArraysLayout layout(pointer_size, dex_file); - bss_size_ += layout.Size(); + if (!HasBootImage()) { + PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); + for (const DexFile* dex_file : *dex_files_) { + dex_cache_arrays_offsets_.Put(dex_file, bss_start_ + bss_size_); + DexCacheArraysLayout layout(pointer_size, dex_file); + bss_size_ += layout.Size(); + } } bss_roots_offset_ = bss_size_; + // Prepare offsets for .bss Class entries. + for (auto& entry : bss_type_entries_) { + DCHECK_EQ(entry.second, 0u); + entry.second = bss_start_ + bss_size_; + bss_size_ += sizeof(GcRoot<mirror::Class>); + } // Prepare offsets for .bss String entries. for (auto& entry : bss_string_entries_) { DCHECK_EQ(entry.second, 0u); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 8d087f4f91..db84166ad3 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -31,6 +31,7 @@ #include "os.h" #include "safe_map.h" #include "string_reference.h" +#include "utils/type_reference.h" namespace art { @@ -372,6 +373,11 @@ class OatWriter { // The offset of the GC roots in .bss section. size_t bss_roots_offset_; + // Map for allocating Class entries in .bss. Indexed by TypeReference for the source + // type in the dex file with the "type value comparator" for deduplication. The value + // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. + SafeMap<TypeReference, size_t, TypeReferenceValueComparator> bss_type_entries_; + // Map for allocating String entries in .bss. Indexed by StringReference for the source // string in the dex file with the "string value comparator" for deduplication. The value // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index 7dc094b25f..2ee4db923a 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -153,21 +153,6 @@ class ValueBound : public ValueObject { return instruction_ == bound.instruction_ && constant_ == bound.constant_; } - /* - * Hunt "under the hood" of array lengths (leading to array references), - * null checks (also leading to array references), and new arrays - * (leading to the actual length). This makes it more likely related - * instructions become actually comparable. - */ - static HInstruction* HuntForDeclaration(HInstruction* instruction) { - while (instruction->IsArrayLength() || - instruction->IsNullCheck() || - instruction->IsNewArray()) { - instruction = instruction->InputAt(0); - } - return instruction; - } - static bool Equal(HInstruction* instruction1, HInstruction* instruction2) { if (instruction1 == instruction2) { return true; @@ -1136,7 +1121,7 @@ class BCEVisitor : public HGraphVisitor { } void VisitNewArray(HNewArray* new_array) OVERRIDE { - HInstruction* len = new_array->InputAt(0); + HInstruction* len = new_array->GetLength(); if (!len->IsIntConstant()) { HInstruction *left; int32_t right_const; @@ -1324,7 +1309,7 @@ class BCEVisitor : public HGraphVisitor { InductionVarRange::Value v2; bool needs_finite_test = false; HInstruction* index = context->InputAt(0); - HInstruction* hint = ValueBound::HuntForDeclaration(context->InputAt(1)); + HInstruction* hint = HuntForDeclaration(context->InputAt(1)); if (induction_range_.GetInductionRange(context, index, hint, &v1, &v2, &needs_finite_test)) { if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) && v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) { diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index dfa150406d..5d58207511 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -596,13 +596,11 @@ static HInstruction* BuildSSAGraph3(HGraph* graph, HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); entry->AddSuccessor(block); + // We pass a bogus constant for the class to avoid mocking one. HInstruction* new_array = new (allocator) HNewArray( constant_10, - graph->GetCurrentMethod(), - 0, - dex::TypeIndex(static_cast<uint16_t>(Primitive::kPrimInt)), - graph->GetDexFile(), - kQuickAllocArray); + constant_10, + 0); block->AddInstruction(new_array); block->AddInstruction(new (allocator) HGoto()); diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index f896f1199e..8cf4089eba 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -63,7 +63,8 @@ class HGraphBuilder : public ValueObject { driver, interpreter_metadata, compiler_stats, - dex_cache) {} + dex_cache, + handles) {} // Only for unit testing. HGraphBuilder(HGraph* graph, @@ -90,7 +91,8 @@ class HGraphBuilder : public ValueObject { /* compiler_driver */ nullptr, /* interpreter_metadata */ nullptr, /* compiler_stats */ nullptr, - null_dex_cache_) {} + null_dex_cache_, + handles) {} GraphAnalysisResult BuildGraph(); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index f00648f570..70c2738010 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -367,6 +367,12 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); } +void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) { + MoveConstant(invoke->GetLocations()->GetTemp(0), static_cast<int32_t>(invoke->GetType())); + QuickEntrypointEnum entrypoint = kQuickInvokePolymorphic; + InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); +} + void CodeGenerator::CreateUnresolvedFieldLocationSummary( HInstruction* field_access, Primitive::Type field_type, @@ -491,31 +497,34 @@ void CodeGenerator::GenerateUnresolvedFieldAccess( } } -// TODO: Remove argument `code_generator_supports_read_barrier` when -// all code generators have read barrier support. -void CodeGenerator::CreateLoadClassLocationSummary(HLoadClass* cls, - Location runtime_type_index_location, - Location runtime_return_location, - bool code_generator_supports_read_barrier) { - ArenaAllocator* allocator = cls->GetBlock()->GetGraph()->GetArena(); - LocationSummary::CallKind call_kind = cls->NeedsAccessCheck() - ? LocationSummary::kCallOnMainOnly - : (((code_generator_supports_read_barrier && kEmitCompilerReadBarrier) || - cls->CanCallRuntime()) - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall); - LocationSummary* locations = new (allocator) LocationSummary(cls, call_kind); +void CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(HLoadClass* cls, + Location runtime_type_index_location, + Location runtime_return_location) { + DCHECK_EQ(cls->GetLoadKind(), HLoadClass::LoadKind::kDexCacheViaMethod); + DCHECK_EQ(cls->InputCount(), 1u); + LocationSummary* locations = new (cls->GetBlock()->GetGraph()->GetArena()) LocationSummary( + cls, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_type_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { + DCHECK_EQ(cls->GetLoadKind(), HLoadClass::LoadKind::kDexCacheViaMethod); + LocationSummary* locations = cls->GetLocations(); + MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); if (cls->NeedsAccessCheck()) { - locations->SetInAt(0, Location::NoLocation()); - locations->AddTemp(runtime_type_index_location); - locations->SetOut(runtime_return_location); + CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); + InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); + } else if (cls->MustGenerateClinitCheck()) { + CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); + InvokeRuntime(kQuickInitializeStaticStorage, cls, cls->GetDexPc()); } else { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); + InvokeRuntime(kQuickInitializeType, cls, cls->GetDexPc()); } } - void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const { // The DCHECKS below check that a register is not specified twice in // the summary. The out location can overlap with an input, so we need @@ -927,10 +936,10 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo if (environment->GetParent() != nullptr) { // We emit the parent environment first. EmitEnvironment(environment->GetParent(), slow_path); - stack_map_stream_.BeginInlineInfoEntry(environment->GetMethodIdx(), + stack_map_stream_.BeginInlineInfoEntry(environment->GetMethod(), environment->GetDexPc(), - environment->GetInvokeType(), - environment->Size()); + environment->Size(), + &graph_->GetDexFile()); } // Walk over the environment, and record the location of dex registers. diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 6366b9838f..38d532e1e9 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -426,12 +426,12 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { } - // Perfoms checks pertaining to an InvokeRuntime call. + // Performs checks pertaining to an InvokeRuntime call. void ValidateInvokeRuntime(QuickEntrypointEnum entrypoint, HInstruction* instruction, SlowPathCode* slow_path); - // Perfoms checks pertaining to an InvokeRuntimeWithoutRecordingPcInfo call. + // Performs checks pertaining to an InvokeRuntimeWithoutRecordingPcInfo call. static void ValidateInvokeRuntimeWithoutRecordingPcInfo(HInstruction* instruction, SlowPathCode* slow_path); @@ -495,6 +495,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke); + void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke); + void CreateUnresolvedFieldLocationSummary( HInstruction* field_access, Primitive::Type field_type, @@ -507,11 +509,10 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { uint32_t dex_pc, const FieldAccessCallingConvention& calling_convention); - // TODO: This overlaps a bit with MoveFromReturnRegister. Refactor for a better design. - static void CreateLoadClassLocationSummary(HLoadClass* cls, - Location runtime_type_index_location, - Location runtime_return_location, - bool code_generator_supports_read_barrier = false); + static void CreateLoadClassRuntimeCallLocationSummary(HLoadClass* cls, + Location runtime_type_index_location, + Location runtime_return_location); + void GenerateLoadClassRuntimeCall(HLoadClass* cls); static void CreateSystemArrayCopyLocationSummary(HInvoke* invoke); @@ -521,7 +522,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { virtual void InvokeRuntime(QuickEntrypointEnum entrypoint, HInstruction* instruction, uint32_t dex_pc, - SlowPathCode* slow_path) = 0; + SlowPathCode* slow_path = nullptr) = 0; // Check if the desired_string_load_kind is supported. If it is, return it, // otherwise return a fall-back kind that should be used instead. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 541a1c5b8f..9c9c604dca 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -371,22 +371,23 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCodeARM(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeARM(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); + dex::TypeIndex type_index = cls_->GetTypeIndex(); + __ LoadImmediate(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; - arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); + arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this); if (do_clinit_) { CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { @@ -400,6 +401,23 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); } RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to + // kSaveEverything and use a temporary for the .bss entry address in the fast path, + // so that we can avoid another calculation here. + CodeGeneratorARM::PcRelativePatchInfo* labels = + arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); + __ BindTrackedLabel(&labels->movw_label); + __ movw(IP, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->movt_label); + __ movt(IP, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->add_pc_label); + __ add(IP, IP, ShifterOperand(PC)); + __ str(locations->Out().AsRegister<Register>(), Address(IP)); + } __ b(GetExitLabel()); } @@ -409,10 +427,6 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -430,7 +444,7 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); HLoadString* load = instruction_->AsLoadString(); - const uint32_t string_index = load->GetStringIndex().index_; + const dex::StringIndex string_index = load->GetStringIndex(); Register out = locations->Out().AsRegister<Register>(); Register temp = locations->GetTemp(0).AsRegister<Register>(); constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); @@ -449,7 +463,7 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { __ mov(entry_address, ShifterOperand(temp)); } - __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index); + __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index.index_); arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -1208,6 +1222,7 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, boot_image_type_patches_(TypeReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), @@ -2370,6 +2385,14 @@ void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderARM::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); @@ -3961,19 +3984,16 @@ void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(Location::RegisterLocation(R0)); - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { - InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); } void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { @@ -5708,17 +5728,11 @@ HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kJitTableAddress: - break; - case HLoadClass::LoadKind::kDexCachePcRelative: + case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); - // We disable pc-relative load when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods - // with irreducible loops. - if (GetGraph()->HasIrreducibleLoops()) { - return HLoadClass::LoadKind::kDexCacheViaMethod; - } + break; + case HLoadClass::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); break; case HLoadClass::LoadKind::kDexCacheViaMethod: break; @@ -5727,15 +5741,16 @@ HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind( } void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(R0), - /* code_generator_supports_read_barrier */ true); + Location::RegisterLocation(R0)); return; } + DCHECK(!cls->NeedsAccessCheck()); const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage(); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier) @@ -5746,24 +5761,23 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kReferrersClass || - load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || - load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + if (load_kind == HLoadClass::LoadKind::kReferrersClass) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { - LocationSummary* locations = cls->GetLocations(); - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); + LocationSummary* locations = cls->GetLocations(); Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); @@ -5771,7 +5785,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - switch (cls->GetLoadKind()) { + switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); @@ -5785,12 +5799,14 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), cls->GetTypeIndex())); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); CodeGeneratorARM::PcRelativePatchInfo* labels = codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); @@ -5804,41 +5820,36 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } + case HLoadClass::LoadKind::kBssEntry: { + CodeGeneratorARM::PcRelativePatchInfo* labels = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + __ BindTrackedLabel(&labels->movw_label); + __ movw(out, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->movt_label); + __ movt(out, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->add_pc_label); + __ add(out, out, ShifterOperand(PC)); + GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption); + generate_null_check = true; + break; + } case HLoadClass::LoadKind::kJitTableAddress: { __ LoadLiteral(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), - cls->GetAddress())); + cls->GetClass())); // /* GcRoot<mirror::Class> */ out = *out GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); break; } - case HLoadClass::LoadKind::kDexCachePcRelative: { - Register base_reg = locations->InAt(0).AsRegister<Register>(); - HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase(); - int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset(); - // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) - GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, read_barrier_option); - generate_null_check = !cls->IsInDexCache(); - break; - } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - Register current_method = locations->InAt(0).AsRegister<Register>(); - __ LoadFromOffset(kLoadWord, - out, - current_method, - ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); - // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); - GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option); - generate_null_check = !cls->IsInDexCache(); - } + case HLoadClass::LoadKind::kDexCacheViaMethod: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } if (generate_null_check || cls->MustGenerateClinitCheck()) { @@ -5946,6 +5957,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_S switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), load->GetStringIndex())); return; // No dex cache slow path. @@ -5953,7 +5965,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_S case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARM::PcRelativePatchInfo* labels = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); __ BindTrackedLabel(&labels->movw_label); __ movw(out, /* placeholder */ 0u); __ BindTrackedLabel(&labels->movt_label); @@ -5973,7 +5985,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_S DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); Register temp = locations->GetTemp(0).AsRegister<Register>(); CodeGeneratorARM::PcRelativePatchInfo* labels = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); __ BindTrackedLabel(&labels->movw_label); __ movw(temp, /* placeholder */ 0u); __ BindTrackedLabel(&labels->movt_label); @@ -7279,8 +7291,8 @@ void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch( - const DexFile& dex_file, uint32_t string_index) { - return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); + const DexFile& dex_file, dex::StringIndex string_index) { + return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_); } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( @@ -7288,6 +7300,11 @@ CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } +CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewTypeBssEntryPatch( + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_); +} + CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch( const DexFile& dex_file, uint32_t element_offset) { return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); @@ -7331,8 +7348,9 @@ Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, Literal* CodeGeneratorARM::DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); return jit_class_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -7366,6 +7384,7 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() + boot_image_type_patches_.size() + /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() + + /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, @@ -7380,12 +7399,17 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche target_string.string_index.index_)); } if (!GetCompilerOptions().IsBootImage()) { + DCHECK(pc_relative_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, linker_patches); } else { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, linker_patches); } + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, + linker_patches); for (const auto& entry : boot_image_type_patches_) { const TypeReference& target_type = entry.first; Literal* literal = entry.second; @@ -7395,8 +7419,6 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche target_type.dex_file, target_type.type_index.index_)); } - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); Literal* literal = entry.second; @@ -7404,6 +7426,7 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche uint32_t literal_offset = literal->GetLabel()->Position(); linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); } + DCHECK_EQ(size, linker_patches->size()); } Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index d5968e0764..52d18575ff 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -481,8 +481,10 @@ class CodeGeneratorARM : public CodeGenerator { Label add_pc_label; }; - PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, + dex::StringIndex string_index); PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, @@ -494,7 +496,7 @@ class CodeGeneratorARM : public CodeGenerator { Handle<mirror::String> handle); Literal* DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address); + Handle<mirror::Class> handle); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; @@ -635,8 +637,10 @@ class CodeGeneratorARM : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. TypeToLiteralMap boot_image_type_patches_; - // PC-relative type patch info. + // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // PC-relative type patch info for kBssEntry. + ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9aaeadb44a..68d0b869e7 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -276,22 +276,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCodeARM64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeARM64(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex().index_); + dex::TypeIndex type_index = cls_->GetTypeIndex(); + __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; - arm64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); + arm64_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this); if (do_clinit_) { CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { @@ -302,11 +303,32 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { Location out = locations->Out(); if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); - Primitive::Type type = at_->GetType(); + Primitive::Type type = instruction_->GetType(); arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); } - RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler()); + Register temp = temps.AcquireX(); + const DexFile& dex_file = cls_->GetDexFile(); + // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to + // kSaveEverything and use a temporary for the ADRP in the fast path, so that we + // can avoid the ADRP here. + vixl::aarch64::Label* adrp_label = + arm64_codegen->NewBssEntryTypePatch(dex_file, type_index); + arm64_codegen->EmitAdrpPlaceholder(adrp_label, temp); + vixl::aarch64::Label* strp_label = + arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, adrp_label); + { + SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler()); + __ Bind(strp_label); + __ str(RegisterFrom(locations->Out(), Primitive::kPrimNot), + MemOperand(temp, /* offset placeholder */ 0)); + } + } __ B(GetExitLabel()); } @@ -316,10 +338,6 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -349,8 +367,8 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; - __ Mov(calling_convention.GetRegisterAt(0).W(), string_index); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); + __ Mov(calling_convention.GetRegisterAt(0).W(), string_index.index_); arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); Primitive::Type type = instruction_->GetType(); @@ -1154,6 +1172,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, boot_image_type_patches_(TypeReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), @@ -3994,7 +4013,7 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { // Add ADRP with its PC-relative DexCache access patch. - const DexFile& dex_file = invoke->GetDexFile(); + const DexFile& dex_file = invoke->GetDexFileForPcRelativeDexCache(); uint32_t element_offset = invoke->GetDexCacheArrayOffset(); vixl::aarch64::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset); EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); @@ -4080,11 +4099,20 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te __ Blr(lr); } +void LocationsBuilderARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeStringPatch( const DexFile& dex_file, - uint32_t string_index, + dex::StringIndex string_index, vixl::aarch64::Label* adrp_label) { - return NewPcRelativePatch(dex_file, string_index, adrp_label, &pc_relative_string_patches_); + return + NewPcRelativePatch(dex_file, string_index.index_, adrp_label, &pc_relative_string_patches_); } vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeTypePatch( @@ -4094,6 +4122,13 @@ vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeTypePatch( return NewPcRelativePatch(dex_file, type_index.index_, adrp_label, &pc_relative_type_patches_); } +vixl::aarch64::Label* CodeGeneratorARM64::NewBssEntryTypePatch( + const DexFile& dex_file, + dex::TypeIndex type_index, + vixl::aarch64::Label* adrp_label) { + return NewPcRelativePatch(dex_file, type_index.index_, adrp_label, &type_bss_entry_patches_); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch( const DexFile& dex_file, uint32_t element_offset, @@ -4146,8 +4181,9 @@ vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLitera } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitClassLiteral( - const DexFile& dex_file, dex::TypeIndex type_index, uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + const DexFile& dex_file, dex::TypeIndex type_index, Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); return jit_class_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); @@ -4200,6 +4236,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc pc_relative_string_patches_.size() + boot_image_type_patches_.size() + pc_relative_type_patches_.size() + + type_bss_entry_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) { @@ -4216,12 +4253,17 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc target_string.string_index.index_)); } if (!GetCompilerOptions().IsBootImage()) { + DCHECK(pc_relative_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, linker_patches); } else { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, linker_patches); } + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, + linker_patches); for (const auto& entry : boot_image_type_patches_) { const TypeReference& target_type = entry.first; vixl::aarch64::Literal<uint32_t>* literal = entry.second; @@ -4229,13 +4271,12 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc target_type.dex_file, target_type.type_index.index_)); } - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); vixl::aarch64::Literal<uint32_t>* literal = entry.second; linker_patches->push_back(LinkerPatch::RecordPosition(literal->GetOffset())); } + DCHECK_EQ(size, linker_patches->size()); } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value, @@ -4298,12 +4339,12 @@ HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; + case HLoadClass::LoadKind::kBssEntry: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); break; - case HLoadClass::LoadKind::kDexCachePcRelative: - DCHECK(!Runtime::Current()->UseJitCompilation()); - break; case HLoadClass::LoadKind::kDexCacheViaMethod: break; } @@ -4311,15 +4352,16 @@ HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( } void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, LocationFrom(calling_convention.GetRegisterAt(0)), - LocationFrom(vixl::aarch64::x0), - /* code_generator_supports_read_barrier */ true); + LocationFrom(vixl::aarch64::x0)); return; } + DCHECK(!cls->NeedsAccessCheck()); const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage(); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier) @@ -4330,21 +4372,21 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kReferrersClass || - load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + if (load_kind == HLoadClass::LoadKind::kReferrersClass) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); Location out_loc = cls->GetLocations()->Out(); Register out = OutputRegister(cls); @@ -4353,7 +4395,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - switch (cls->GetLoadKind()) { + switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); @@ -4387,59 +4429,46 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress())); - __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress())); - break; - } - case HLoadClass::LoadKind::kJitTableAddress: { - __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), - cls->GetTypeIndex(), - cls->GetAddress())); - GenerateGcRootFieldLoad(cls, - out_loc, - out.X(), - /* offset */ 0, - /* fixup_label */ nullptr, - kCompilerReadBarrierOption); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); + __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kDexCachePcRelative: { - // Add ADRP with its PC-relative DexCache access patch. + case HLoadClass::LoadKind::kBssEntry: { + // Add ADRP with its PC-relative Class .bss entry patch. const DexFile& dex_file = cls->GetDexFile(); - uint32_t element_offset = cls->GetDexCacheElementOffset(); - vixl::aarch64::Label* adrp_label = - codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset); + dex::TypeIndex type_index = cls->GetTypeIndex(); + vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); - // Add LDR with its PC-relative DexCache access patch. + // Add LDR with its PC-relative Class patch. vixl::aarch64::Label* ldr_label = - codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label); + codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(cls, - out_loc, + cls->GetLocations()->Out(), out.X(), - /* offset placeholder */ 0, + /* placeholder */ 0u, ldr_label, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + kCompilerReadBarrierOption); + generate_null_check = true; break; } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - MemberOffset resolved_types_offset = - ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize); - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - Register current_method = InputRegisterAt(cls, 0); - __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value())); - // /* GcRoot<mirror::Class> */ out = out[type_index] + case HLoadClass::LoadKind::kJitTableAddress: { + __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), + cls->GetTypeIndex(), + cls->GetClass())); GenerateGcRootFieldLoad(cls, out_loc, out.X(), - CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_), + /* offset */ 0, /* fixup_label */ nullptr, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + kCompilerReadBarrierOption); break; } + case HLoadClass::LoadKind::kDexCacheViaMethod: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } if (generate_null_check || cls->MustGenerateClinitCheck()) { @@ -4494,11 +4523,11 @@ HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind( case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; - case HLoadString::LoadKind::kDexCacheViaMethod: - break; case HLoadString::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); break; + case HLoadString::LoadKind::kDexCacheViaMethod: + break; } return desired_string_load_kind; } @@ -4542,7 +4571,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { // Add ADRP with its PC-relative String patch. const DexFile& dex_file = load->GetDexFile(); - uint32_t string_index = load->GetStringIndex().index_; + const dex::StringIndex string_index = load->GetStringIndex(); DCHECK(codegen_->GetCompilerOptions().IsBootImage()); vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); @@ -4562,7 +4591,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD case HLoadString::LoadKind::kBssEntry: { // Add ADRP with its PC-relative String .bss entry patch. const DexFile& dex_file = load->GetDexFile(); - uint32_t string_index = load->GetStringIndex().index_; + const dex::StringIndex string_index = load->GetStringIndex(); DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); Register temp = temps.AcquireX(); @@ -4718,22 +4747,16 @@ void LocationsBuilderARM64::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetOut(LocationFrom(x0)); - locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1))); - locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); } void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) { - LocationSummary* locations = instruction->GetLocations(); - InvokeRuntimeCallingConvention calling_convention; - Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); - DCHECK(type_index.Is(w0)); - __ Mov(type_index, instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); } void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index d6a5f9d1fa..a9dca92980 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -540,7 +540,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing // to the associated ADRP patch label). vixl::aarch64::Label* NewPcRelativeStringPatch(const DexFile& dex_file, - uint32_t string_index, + dex::StringIndex string_index, vixl::aarch64::Label* adrp_label = nullptr); // Add a new PC-relative type patch for an instruction and return the label @@ -551,6 +551,14 @@ class CodeGeneratorARM64 : public CodeGenerator { dex::TypeIndex type_index, vixl::aarch64::Label* adrp_label = nullptr); + // Add a new .bss entry type patch for an instruction and return the label + // to be bound before the instruction. The instruction will be either the + // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing + // to the associated ADRP patch label). + vixl::aarch64::Label* NewBssEntryTypePatch(const DexFile& dex_file, + dex::TypeIndex type_index, + vixl::aarch64::Label* adrp_label = nullptr); + // Add a new PC-relative dex cache array patch for an instruction and return // the label to be bound before the instruction. The instruction will be // either the ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label` @@ -571,7 +579,7 @@ class CodeGeneratorARM64 : public CodeGenerator { Handle<mirror::String> handle); vixl::aarch64::Literal<uint32_t>* DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex string_index, - uint64_t address); + Handle<mirror::Class> handle); void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg); void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, @@ -744,8 +752,10 @@ class CodeGeneratorARM64 : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. TypeToLiteralMap boot_image_type_patches_; - // PC-relative type patch info. + // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // PC-relative type patch info for kBssEntry. + ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index c769decaa0..592ee5aba6 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -394,22 +394,23 @@ class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL { class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { public: LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCodeARMVIXL(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeARMVIXL(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); + dex::TypeIndex type_index = cls_->GetTypeIndex(); + __ Mov(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; - arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); + arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this); if (do_clinit_) { CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { @@ -423,6 +424,20 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { arm_codegen->Move32(locations->Out(), LocationFrom(r0)); } RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to + // kSaveEverything and use a temporary for the .bss entry address in the fast path, + // so that we can avoid another calculation here. + UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()); + vixl32::Register temp = temps.Acquire(); + CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = + arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); + arm_codegen->EmitMovwMovtPlaceholder(labels, temp); + __ Str(OutputRegister(cls_), MemOperand(temp)); + } __ B(GetExitLabel()); } @@ -432,10 +447,6 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -454,7 +465,7 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); HLoadString* load = instruction_->AsLoadString(); - const uint32_t string_index = load->GetStringIndex().index_; + const dex::StringIndex string_index = load->GetStringIndex(); vixl32::Register out = OutputRegister(load); vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); @@ -473,7 +484,7 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { __ Mov(entry_address, temp); } - __ Mov(calling_convention.GetRegisterAt(0), string_index); + __ Mov(calling_convention.GetRegisterAt(0), string_index.index_); arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -1252,6 +1263,7 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, boot_image_type_patches_(TypeReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), @@ -2445,6 +2457,14 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* inv } } +void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); @@ -3977,19 +3997,16 @@ void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConventionARMVIXL calling_convention; - locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetOut(LocationFrom(r0)); locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1))); locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2))); } void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) { - InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); } void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) { @@ -5789,17 +5806,11 @@ HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kJitTableAddress: - break; - case HLoadClass::LoadKind::kDexCachePcRelative: + case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); - // We disable pc-relative load when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods - // with irreducible loops. - if (GetGraph()->HasIrreducibleLoops()) { - return HLoadClass::LoadKind::kDexCacheViaMethod; - } + break; + case HLoadClass::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); break; case HLoadClass::LoadKind::kDexCacheViaMethod: break; @@ -5808,15 +5819,16 @@ HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind( } void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConventionARMVIXL calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, LocationFrom(calling_convention.GetRegisterAt(0)), - LocationFrom(r0), - /* code_generator_supports_read_barrier */ true); + LocationFrom(r0)); return; } + DCHECK(!cls->NeedsAccessCheck()); const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage(); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier) @@ -5827,24 +5839,23 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kReferrersClass || - load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || - load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + if (load_kind == HLoadClass::LoadKind::kReferrersClass) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { - LocationSummary* locations = cls->GetLocations(); - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); + LocationSummary* locations = cls->GetLocations(); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(cls); @@ -5852,7 +5863,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - switch (cls->GetLoadKind()) { + switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); @@ -5866,12 +5877,14 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), cls->GetTypeIndex())); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); @@ -5880,43 +5893,31 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } + case HLoadClass::LoadKind::kBssEntry: { + CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->EmitMovwMovtPlaceholder(labels, out); + GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption); + generate_null_check = true; + break; + } case HLoadClass::LoadKind::kJitTableAddress: { __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), - cls->GetAddress())); + cls->GetClass())); // /* GcRoot<mirror::Class> */ out = *out GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); break; } - case HLoadClass::LoadKind::kDexCachePcRelative: { - vixl32::Register base_reg = InputRegisterAt(cls, 0); - HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase(); - int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset(); - // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) - GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, read_barrier_option); - generate_null_check = !cls->IsInDexCache(); - break; - } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - vixl32::Register current_method = InputRegisterAt(cls, 0); - const int32_t resolved_types_offset = - ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value(); - GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset); - // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); - GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option); - generate_null_check = !cls->IsInDexCache(); - break; - } - default: - TODO_VIXL32(FATAL); + case HLoadClass::LoadKind::kDexCacheViaMethod: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } if (generate_null_check || cls->MustGenerateClinitCheck()) { @@ -6038,7 +6039,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, out); return; // No dex cache slow path. } @@ -6053,7 +6054,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, temp); GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = @@ -7397,8 +7398,8 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location } CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeStringPatch( - const DexFile& dex_file, uint32_t string_index) { - return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); + const DexFile& dex_file, dex::StringIndex string_index) { + return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_); } CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTypePatch( @@ -7406,6 +7407,11 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTy return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } +CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch( + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_); +} + CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeDexCacheArrayPatch( const DexFile& dex_file, uint32_t element_offset) { return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); @@ -7462,8 +7468,9 @@ VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral( VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); return jit_class_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { @@ -7499,6 +7506,7 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pa /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() + boot_image_type_patches_.size() + /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() + + /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, @@ -7513,12 +7521,17 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pa target_string.string_index.index_)); } if (!GetCompilerOptions().IsBootImage()) { + DCHECK(pc_relative_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, linker_patches); } else { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, linker_patches); } + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, + linker_patches); for (const auto& entry : boot_image_type_patches_) { const TypeReference& target_type = entry.first; VIXLUInt32Literal* literal = entry.second; @@ -7528,8 +7541,6 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pa target_type.dex_file, target_type.type_index.index_)); } - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); VIXLUInt32Literal* literal = entry.second; @@ -7537,6 +7548,7 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pa uint32_t literal_offset = literal->GetLocation(); linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); } + DCHECK_EQ(size, linker_patches->size()); } VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal( diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 200a463c75..be653535ea 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -562,8 +562,10 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Label add_pc_label; }; - PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, + dex::StringIndex string_index); PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); VIXLUInt32Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, @@ -577,7 +579,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { Handle<mirror::String> handle); VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address); + Handle<mirror::Class> handle); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; @@ -731,8 +733,10 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. TypeToLiteralMap boot_image_type_patches_; - // PC-relative type patch info. + // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // PC-relative type patch info for kBssEntry. + ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index bc62854e5d..a0383826d3 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -213,23 +213,24 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCodeMIPS(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeMIPS(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); + dex::TypeIndex type_index = cls_->GetTypeIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; - mips_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); + mips_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this); if (do_clinit_) { CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { @@ -240,11 +241,26 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { Location out = locations->Out(); if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); - Primitive::Type type = at_->GetType(); + Primitive::Type type = instruction_->GetType(); mips_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); } RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to + // kSaveEverything and use a temporary for the .bss entry address in the fast path, + // so that we can avoid another calculation here. + bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); + Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + DCHECK_NE(out.AsRegister<Register>(), AT); + CodeGeneratorMIPS::PcRelativePatchInfo* info = + mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); + mips_codegen->EmitPcRelativeAddressPlaceholder(info, TMP, base); + __ StoreToOffset(kStoreWord, out.AsRegister<Register>(), TMP, 0); + } __ B(GetExitLabel()); } @@ -254,10 +270,6 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -281,8 +293,8 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { InvokeRuntimeCallingConvention calling_convention; HLoadString* load = instruction_->AsLoadString(); - const uint32_t string_index = load->GetStringIndex().index_; - __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); + const dex::StringIndex string_index = load->GetStringIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_); mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); Primitive::Type type = instruction_->GetType(); @@ -465,6 +477,7 @@ CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph, boot_image_type_patches_(TypeReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), clobbered_ra_(false) { @@ -1007,6 +1020,7 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch pc_relative_dex_cache_patches_.size() + pc_relative_string_patches_.size() + pc_relative_type_patches_.size() + + type_bss_entry_patches_.size() + boot_image_string_patches_.size() + boot_image_type_patches_.size() + boot_image_address_patches_.size(); @@ -1014,13 +1028,16 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); if (!GetCompilerOptions().IsBootImage()) { + DCHECK(pc_relative_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, linker_patches); } else { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, linker_patches); } - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, linker_patches); for (const auto& entry : boot_image_string_patches_) { const StringReference& target_string = entry.first; @@ -1047,11 +1064,12 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); } + DCHECK_EQ(size, linker_patches->size()); } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeStringPatch( - const DexFile& dex_file, uint32_t string_index) { - return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); + const DexFile& dex_file, dex::StringIndex string_index) { + return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_); } CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch( @@ -1059,6 +1077,11 @@ CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatc return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } +CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewTypeBssEntryPatch( + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_); +} + CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeDexCacheArrayPatch( const DexFile& dex_file, uint32_t element_offset) { return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); @@ -5154,6 +5177,14 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo } } +void LocationsBuilderMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS intrinsic(codegen); @@ -5186,14 +5217,14 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; - case HLoadString::LoadKind::kDexCacheViaMethod: - fallback_load = false; - break; case HLoadString::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); // TODO: implement. fallback_load = true; break; + case HLoadString::LoadKind::kDexCacheViaMethod: + fallback_load = false; + break; } if (fallback_load) { desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; @@ -5222,15 +5253,13 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; + case HLoadClass::LoadKind::kBssEntry: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); fallback_load = true; break; - case HLoadClass::LoadKind::kDexCachePcRelative: - DCHECK(!Runtime::Current()->UseJitCompilation()); - // TODO: Create as many MipsDexCacheArraysBase instructions as needed for methods - // with irreducible loops. - break; case HLoadClass::LoadKind::kDexCacheViaMethod: fallback_load = false; break; @@ -5427,34 +5456,32 @@ void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { } void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(V0), - /* code_generator_supports_read_barrier */ false); // TODO: revisit this bool. + Location::RegisterLocation(V0)); return; } + DCHECK(!cls->NeedsAccessCheck()); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); switch (load_kind) { // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimeAddress: - case HLoadClass::LoadKind::kBootImageAddress: case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBssEntry: if (codegen_->GetInstructionSetFeatures().IsR6()) { break; } FALLTHROUGH_INTENDED; - // We need an extra register for PC-relative dex cache accesses. - case HLoadClass::LoadKind::kDexCachePcRelative: case HLoadClass::LoadKind::kReferrersClass: - case HLoadClass::LoadKind::kDexCacheViaMethod: locations->SetInAt(0, Location::RequiresRegister()); break; default: @@ -5463,16 +5490,17 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { - LocationSummary* locations = cls->GetLocations(); - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + LocationSummary* locations = cls->GetLocations(); Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); Register base_or_current_method_reg; @@ -5480,12 +5508,11 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { switch (load_kind) { // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimeAddress: - case HLoadClass::LoadKind::kBootImageAddress: case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBssEntry: base_or_current_method_reg = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); break; - // We need an extra register for PC-relative dex cache accesses. - case HLoadClass::LoadKind::kDexCachePcRelative: case HLoadClass::LoadKind::kReferrersClass: case HLoadClass::LoadKind::kDexCacheViaMethod: base_or_current_method_reg = locations->InAt(0).AsRegister<Register>(); @@ -5508,14 +5535,14 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageLinkTimeAddress: - DCHECK(!kEmitCompilerReadBarrier); + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ LoadLiteral(out, base_or_current_method_reg, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), cls->GetTypeIndex())); break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { - DCHECK(!kEmitCompilerReadBarrier); + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); @@ -5523,38 +5550,29 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK(!kEmitCompilerReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ LoadLiteral(out, base_or_current_method_reg, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kJitTableAddress: { - LOG(FATAL) << "Unimplemented"; + case HLoadClass::LoadKind::kBssEntry: { + CodeGeneratorMIPS::PcRelativePatchInfo* info = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); + __ LoadFromOffset(kLoadWord, out, out, 0); + generate_null_check = true; break; } - case HLoadClass::LoadKind::kDexCachePcRelative: { - HMipsDexCacheArraysBase* base = cls->InputAt(0)->AsMipsDexCacheArraysBase(); - int32_t offset = - cls->GetDexCacheElementOffset() - base->GetElementOffset() - kDexCacheArrayLwOffset; - // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) - GenerateGcRootFieldLoad(cls, out_loc, base_or_current_method_reg, offset); - generate_null_check = !cls->IsInDexCache(); + case HLoadClass::LoadKind::kJitTableAddress: { + LOG(FATAL) << "Unimplemented"; break; } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - __ LoadFromOffset(kLoadWord, - out, - base_or_current_method_reg, - ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); - // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); - GenerateGcRootFieldLoad(cls, out_loc, out, offset); - generate_null_check = !cls->IsInDexCache(); - } + case HLoadClass::LoadKind::kDexCacheViaMethod: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } if (generate_null_check || cls->MustGenerateClinitCheck()) { @@ -5649,6 +5667,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ LoadLiteral(out, base_or_current_method_reg, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), @@ -5657,7 +5676,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); return; // No dex cache slow path. } @@ -5673,7 +5692,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); __ LoadFromOffset(kLoadWord, out, out, 0); SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load); @@ -5878,21 +5897,14 @@ void LocationsBuilderMIPS::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) { - InvokeRuntimeCallingConvention calling_convention; - Register current_method_register = calling_convention.GetRegisterAt(2); - __ Lw(current_method_register, SP, kCurrentMethodStackOffset); - // Move an uint16_t value to a register. - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_); - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, - void*, uint32_t, int32_t, ArtMethod*>(); + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); } void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 7b0812cb7b..c8fd325999 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -452,8 +452,10 @@ class CodeGeneratorMIPS : public CodeGenerator { MipsLabel pc_rel_label; }; - PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, + dex::StringIndex string_index); PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, @@ -504,8 +506,10 @@ class CodeGeneratorMIPS : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. BootTypeToLiteralMap boot_image_type_patches_; - // PC-relative type patch info. + // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // PC-relative type patch info for kBssEntry. + ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1b9c6da460..446dea659e 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -167,22 +167,23 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCodeMIPS64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeMIPS64(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_); + dex::TypeIndex type_index = cls_->GetTypeIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType; - mips64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this); + mips64_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this); if (do_clinit_) { CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { @@ -193,11 +194,24 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { Location out = locations->Out(); if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); - Primitive::Type type = at_->GetType(); + Primitive::Type type = instruction_->GetType(); mips64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); } RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to + // kSaveEverything and use a temporary for the .bss entry address in the fast path, + // so that we can avoid another calculation here. + DCHECK_NE(out.AsRegister<GpuRegister>(), AT); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); + mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Sw(out.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); + } __ Bc(GetExitLabel()); } @@ -207,10 +221,6 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -234,8 +244,8 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { InvokeRuntimeCallingConvention calling_convention; HLoadString* load = instruction_->AsLoadString(); - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; - __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_); mips64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), @@ -422,6 +432,7 @@ CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, boot_image_type_patches_(TypeReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save RA (containing the return address) to mimic Quick. @@ -922,6 +933,7 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat pc_relative_dex_cache_patches_.size() + pc_relative_string_patches_.size() + pc_relative_type_patches_.size() + + type_bss_entry_patches_.size() + boot_image_string_patches_.size() + boot_image_type_patches_.size() + boot_image_address_patches_.size(); @@ -929,13 +941,16 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); if (!GetCompilerOptions().IsBootImage()) { + DCHECK(pc_relative_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, linker_patches); } else { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, linker_patches); } - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, linker_patches); for (const auto& entry : boot_image_string_patches_) { const StringReference& target_string = entry.first; @@ -962,11 +977,12 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); } + DCHECK_EQ(size, linker_patches->size()); } CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeStringPatch( - const DexFile& dex_file, uint32_t string_index) { - return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); + const DexFile& dex_file, dex::StringIndex string_index) { + return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_); } CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeTypePatch( @@ -974,6 +990,11 @@ CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeType return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); } +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewTypeBssEntryPatch( + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_); +} + CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeDexCacheArrayPatch( const DexFile& dex_file, uint32_t element_offset) { return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); @@ -3095,7 +3116,7 @@ void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad( Location root, GpuRegister obj, uint32_t offset) { - // When handling HLoadClass::LoadKind::kDexCachePcRelative, the caller calls + // When handling PC-relative loads, the caller calls // EmitPcRelativeAddressPlaceholderHigh() and then GenerateGcRootFieldLoad(). // The relative patcher expects the two methods to emit the following patchable // sequence of instructions in this case: @@ -3256,6 +3277,14 @@ void LocationsBuilderMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* in HandleInvoke(invoke); } +void LocationsBuilderMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS64 intrinsic(codegen); @@ -3314,14 +3343,14 @@ HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; + case HLoadClass::LoadKind::kBssEntry: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); // TODO: implement. fallback_load = true; break; - case HLoadClass::LoadKind::kDexCachePcRelative: - DCHECK(!Runtime::Current()->UseJitCompilation()); - break; case HLoadClass::LoadKind::kDexCacheViaMethod: break; } @@ -3366,7 +3395,7 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { uint32_t offset = invoke->GetDexCacheArrayOffset(); CodeGeneratorMIPS64::PcRelativePatchInfo* info = - NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset); + NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset); EmitPcRelativeAddressPlaceholderHigh(info, AT); __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); break; @@ -3474,38 +3503,38 @@ void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) } void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - calling_convention.GetReturnLocation(Primitive::kPrimNot), - /* code_generator_supports_read_barrier */ false); + calling_convention.GetReturnLocation(Primitive::kPrimNot)); return; } + DCHECK(!cls->NeedsAccessCheck()); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kReferrersClass || - load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + if (load_kind == HLoadClass::LoadKind::kReferrersClass) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { - LocationSummary* locations = cls->GetLocations(); - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + LocationSummary* locations = cls->GetLocations(); Location out_loc = locations->Out(); GpuRegister out = out_loc.AsRegister<GpuRegister>(); GpuRegister current_method_reg = ZERO; @@ -3526,14 +3555,14 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { ArtMethod::DeclaringClassOffset().Int32Value()); break; case HLoadClass::LoadKind::kBootImageLinkTimeAddress: - DCHECK(!kEmitCompilerReadBarrier); + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ LoadLiteral(out, kLoadUnsignedWord, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), cls->GetTypeIndex())); break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { - DCHECK(!kEmitCompilerReadBarrier); + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS64::PcRelativePatchInfo* info = codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); @@ -3542,39 +3571,29 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK(!kEmitCompilerReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ LoadLiteral(out, kLoadUnsignedWord, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kJitTableAddress: { - LOG(FATAL) << "Unimplemented"; - break; - } - case HLoadClass::LoadKind::kDexCachePcRelative: { - uint32_t element_offset = cls->GetDexCacheElementOffset(); + case HLoadClass::LoadKind::kBssEntry: { CodeGeneratorMIPS64::PcRelativePatchInfo* info = - codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), element_offset); + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); - // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */ - GenerateGcRootFieldLoad(cls, out_loc, AT, /* placeholder */ 0x5678); - generate_null_check = !cls->IsInDexCache(); + __ Lwu(out, AT, /* placeholder */ 0x5678); + generate_null_check = true; break; } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - __ LoadFromOffset(kLoadDoubleword, - out, - current_method_reg, - ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value()); - // /* GcRoot<mirror::Class> */ out = out[type_index] - size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); - GenerateGcRootFieldLoad(cls, out_loc, out, offset); - generate_null_check = !cls->IsInDexCache(); + case HLoadClass::LoadKind::kJitTableAddress: { + LOG(FATAL) << "Unimplemented"; + break; } + case HLoadClass::LoadKind::kDexCacheViaMethod: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } if (generate_null_check || cls->MustGenerateClinitCheck()) { @@ -3638,6 +3657,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ LoadLiteral(out, kLoadUnsignedWord, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), @@ -3646,7 +3666,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS64::PcRelativePatchInfo* info = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); __ Daddiu(out, AT, /* placeholder */ 0x5678); return; // No dex cache slow path. @@ -3663,7 +3683,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS64::PcRelativePatchInfo* info = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); __ Lwu(out, AT, /* placeholder */ 0x5678); SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load); @@ -3821,19 +3841,14 @@ void LocationsBuilderMIPS64::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) { - LocationSummary* locations = instruction->GetLocations(); - // Move an uint16_t value to a register. - __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), - instruction->GetTypeIndex().index_); - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); } void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 8ac919f47e..52b780c106 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -411,8 +411,10 @@ class CodeGeneratorMIPS64 : public CodeGenerator { Mips64Label pc_rel_label; }; - PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, + dex::StringIndex string_index); PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file, @@ -469,8 +471,10 @@ class CodeGeneratorMIPS64 : public CodeGenerator { ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. BootTypeToLiteralMap boot_image_type_patches_; - // PC-relative type patch info. + // PC-relative type patch info for kBootImageLinkTimePcRelative. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // PC-relative type patch info for kBssEntry. + ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a9b717db4f..853c91fac8 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -225,8 +225,8 @@ class LoadStringSlowPathX86 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; - __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index)); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); + __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index.index_)); x86_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX)); @@ -254,21 +254,24 @@ class LoadClassSlowPathX86 : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex().index_)); + dex::TypeIndex type_index = cls_->GetTypeIndex(); + __ movl(calling_convention.GetRegisterAt(0), Immediate(type_index.index_)); x86_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, - at_, dex_pc_, this); + instruction_, + dex_pc_, + this); if (do_clinit_) { CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); } else { @@ -281,8 +284,17 @@ class LoadClassSlowPathX86 : public SlowPathCode { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); x86_codegen->Move32(out, Location::RegisterLocation(EAX)); } - RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + Register method_address = locations->InAt(0).AsRegister<Register>(); + __ movl(Address(method_address, CodeGeneratorX86::kDummy32BitOffset), + locations->Out().AsRegister<Register>()); + Label* fixup_label = x86_codegen->NewTypeBssEntryPatch(cls_); + __ Bind(fixup_label); + } __ jmp(GetExitLabel()); } @@ -292,10 +304,6 @@ class LoadClassSlowPathX86 : public SlowPathCode { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -1009,7 +1017,8 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), @@ -2244,6 +2253,14 @@ void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderX86::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); @@ -4175,18 +4192,15 @@ void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); locations->SetOut(Location::RegisterLocation(EAX)); InvokeRuntimeCallingConvention calling_convention; - locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { - InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex().index_)); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); DCHECK(!codegen_->IsLeafMethod()); } @@ -4504,7 +4518,7 @@ 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->GetDexFile(), offset)); + __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { @@ -4593,9 +4607,15 @@ void CodeGeneratorX86::RecordBootStringPatch(HLoadString* load_string) { __ Bind(&string_patches_.back().label); } -void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) { - type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); - __ Bind(&type_patches_.back().label); +void CodeGeneratorX86::RecordBootTypePatch(HLoadClass* load_class) { + boot_image_type_patches_.emplace_back(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_); + return &type_bss_entry_patches_.back().label; } Label* CodeGeneratorX86::NewStringBssEntryPatch(HLoadString* load_string) { @@ -4632,7 +4652,8 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche pc_relative_dex_cache_patches_.size() + simple_patches_.size() + string_patches_.size() + - type_patches_.size(); + boot_image_type_patches_.size() + + type_bss_entry_patches_.size(); linker_patches->reserve(size); EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); @@ -4641,24 +4662,26 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); } if (!GetCompilerOptions().IsBootImage()) { + DCHECK(boot_image_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_patches_, linker_patches); } else if (GetCompilerOptions().GetCompilePic()) { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(boot_image_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(string_patches_, linker_patches); } else { + for (const PatchInfo<Label>& info : boot_image_type_patches_) { + uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, &info.dex_file, info.index)); + } for (const PatchInfo<Label>& info : string_patches_) { uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; linker_patches->push_back( LinkerPatch::StringPatch(literal_offset, &info.dex_file, info.index)); } } - if (GetCompilerOptions().GetCompilePic()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(type_patches_, linker_patches); - } else { - for (const PatchInfo<Label>& info : type_patches_) { - uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, &info.dex_file, info.index)); - } - } + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, + linker_patches); + DCHECK_EQ(size, linker_patches->size()); } void CodeGeneratorX86::MarkGCCard(Register temp, @@ -5977,7 +6000,7 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: DCHECK(GetCompilerOptions().GetCompilePic()); FALLTHROUGH_INTENDED; - case HLoadClass::LoadKind::kDexCachePcRelative: + 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. @@ -5999,15 +6022,16 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( } void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(EAX), - /* code_generator_supports_read_barrier */ true); + Location::RegisterLocation(EAX)); return; } + DCHECK(!cls->NeedsAccessCheck()); const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage(); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier) @@ -6018,11 +6042,9 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kReferrersClass || - load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + load_kind == HLoadClass::LoadKind::kBssEntry) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); @@ -6030,23 +6052,26 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); // Add a patch entry and return the label. jit_class_patches_.emplace_back(dex_file, dex_index.index_); PatchInfo<Label>* info = &jit_class_patches_.back(); return &info->label; } -void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { - LocationSummary* locations = cls->GetLocations(); - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); + LocationSummary* locations = cls->GetLocations(); Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); @@ -6054,7 +6079,7 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { const ReadBarrierOption read_barrier_option = cls->IsInBootImage() ? kWithoutReadBarrier : kCompilerReadBarrierOption; - switch (cls->GetLoadKind()) { + switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); @@ -6069,63 +6094,48 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); __ movl(out, Immediate(/* placeholder */ 0)); - codegen_->RecordTypePatch(cls); + codegen_->RecordBootTypePatch(cls); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); Register method_address = locations->InAt(0).AsRegister<Register>(); __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); - codegen_->RecordTypePatch(cls); + codegen_->RecordBootTypePatch(cls); break; } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ movl(out, Immediate(address)); codegen_->RecordSimplePatch(); break; } + case HLoadClass::LoadKind::kBssEntry: { + Register method_address = locations->InAt(0).AsRegister<Register>(); + Address address(method_address, CodeGeneratorX86::kDummy32BitOffset); + Label* fixup_label = codegen_->NewTypeBssEntryPatch(cls); + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option); + generate_null_check = true; + break; + } case HLoadClass::LoadKind::kJitTableAddress: { Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset); Label* fixup_label = codegen_->NewJitRootClassPatch( - cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress()); + cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass()); // /* GcRoot<mirror::Class> */ out = *address GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); break; } - case HLoadClass::LoadKind::kDexCachePcRelative: { - Register base_reg = locations->InAt(0).AsRegister<Register>(); - uint32_t offset = cls->GetDexCacheElementOffset(); - Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset); - // /* GcRoot<mirror::Class> */ out = *(base + offset) /* PC-relative */ - GenerateGcRootFieldLoad(cls, - out_loc, - Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), - fixup_label, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); - break; - } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - Register current_method = locations->InAt(0).AsRegister<Register>(); - __ movl(out, Address(current_method, - ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value())); - // /* GcRoot<mirror::Class> */ out = out[type_index] - GenerateGcRootFieldLoad(cls, - out_loc, - Address(out, - CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)), - /* fixup_label */ nullptr, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); - break; - } + case HLoadClass::LoadKind::kDexCacheViaMethod: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } if (generate_null_check || cls->MustGenerateClinitCheck()) { @@ -6195,11 +6205,11 @@ HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind( break; case HLoadString::LoadKind::kBootImageAddress: break; - case HLoadString::LoadKind::kDexCacheViaMethod: - break; case HLoadString::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); break; + case HLoadString::LoadKind::kDexCacheViaMethod: + break; } return desired_string_load_kind; } @@ -6250,11 +6260,13 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) NO_THREAD_S switch (load->GetLoadKind()) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ movl(out, Immediate(/* placeholder */ 0)); codegen_->RecordBootStringPatch(load); return; // No dex cache slow path. } case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); Register method_address = locations->InAt(0).AsRegister<Register>(); __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); codegen_->RecordBootStringPatch(load); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index dd1628c867..9eb97658da 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -412,13 +412,16 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordSimplePatch(); void RecordBootStringPatch(HLoadString* load_string); - void RecordTypePatch(HLoadClass* load_class); + 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* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index, Handle<mirror::String> handle); - Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address); + Label* NewJitRootClassPatch(const DexFile& dex_file, + dex::TypeIndex dex_index, + Handle<mirror::Class> handle); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; @@ -621,8 +624,10 @@ class CodeGeneratorX86 : public CodeGenerator { ArenaDeque<Label> simple_patches_; // String patch locations; type depends on configuration (app .bss or boot image PIC/non-PIC). ArenaDeque<PatchInfo<Label>> string_patches_; - // Type patch locations. - ArenaDeque<PatchInfo<Label>> type_patches_; + // Type patch locations for boot image; type depends on configuration (boot image PIC/non-PIC). + ArenaDeque<PatchInfo<Label>> boot_image_type_patches_; + // Type patch locations for kBssEntry. + ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_; // Patches for string root accesses in JIT compiled code. ArenaDeque<PatchInfo<Label>> jit_string_patches_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 261473505f..74c71cce1f 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -234,12 +234,12 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - LocationSummary* locations = at_->GetLocations(); + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); @@ -249,7 +249,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls_->GetTypeIndex().index_)); x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, - at_, + instruction_, dex_pc_, this); if (do_clinit_) { @@ -266,6 +266,15 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { } RestoreLiveRegisters(codegen, locations); + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + DCHECK(out.IsValid()); + __ movl(Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false), + locations->Out().AsRegister<CpuRegister>()); + Label* fixup_label = x86_64_codegen->NewTypeBssEntryPatch(cls_); + __ Bind(fixup_label); + } __ jmp(GetExitLabel()); } @@ -275,10 +284,6 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { // The class this slow path will load. HLoadClass* const cls_; - // The instruction where this slow path is happening. - // (Might be the load class or an initialization check). - HInstruction* const at_; - // The dex PC of `at_`. const uint32_t dex_pc_; @@ -300,9 +305,9 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); // Custom calling convention: RAX serves as both input and output. - __ movl(CpuRegister(RAX), Immediate(string_index)); + __ movl(CpuRegister(RAX), Immediate(string_index.index_)); x86_64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), @@ -986,7 +991,7 @@ Location CodeGeneratorX86_64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStat Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); // Bind a new fixup label at the end of the "movl" insn. uint32_t offset = invoke->GetDexCacheArrayOffset(); - __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset)); + __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { @@ -1079,9 +1084,15 @@ void CodeGeneratorX86_64::RecordBootStringPatch(HLoadString* load_string) { __ Bind(&string_patches_.back().label); } -void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) { - type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); - __ Bind(&type_patches_.back().label); +void CodeGeneratorX86_64::RecordBootTypePatch(HLoadClass* load_class) { + boot_image_type_patches_.emplace_back(load_class->GetDexFile(), + load_class->GetTypeIndex().index_); + __ Bind(&boot_image_type_patches_.back().label); +} + +Label* CodeGeneratorX86_64::NewTypeBssEntryPatch(HLoadClass* load_class) { + type_bss_entry_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_); + return &type_bss_entry_patches_.back().label; } Label* CodeGeneratorX86_64::NewStringBssEntryPatch(HLoadString* load_string) { @@ -1118,7 +1129,8 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat pc_relative_dex_cache_patches_.size() + simple_patches_.size() + string_patches_.size() + - type_patches_.size(); + boot_image_type_patches_.size() + + type_bss_entry_patches_.size(); linker_patches->reserve(size); EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); @@ -1127,13 +1139,17 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); } if (!GetCompilerOptions().IsBootImage()) { + DCHECK(boot_image_type_patches_.empty()); EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_patches_, linker_patches); } else { - // These are always PC-relative, see GetSupportedLoadStringKind(). + // These are always PC-relative, see GetSupportedLoadClassKind()/GetSupportedLoadStringKind(). + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(boot_image_type_patches_, + linker_patches); EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(string_patches_, linker_patches); } - // These are always PC-relative, see GetSupportedLoadClassKind(). - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, + linker_patches); + DCHECK_EQ(size, linker_patches->size()); } void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const { @@ -1214,7 +1230,8 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_class_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { @@ -2423,6 +2440,14 @@ void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invo codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86_64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86_64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderX86_64::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); @@ -4063,21 +4088,16 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); InvokeRuntimeCallingConvention calling_convention; - locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetOut(Location::RegisterLocation(RAX)); - locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); } void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { - InvokeRuntimeCallingConvention calling_convention; - codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)), - instruction->GetTypeIndex().index_); // Note: if heap poisoning is enabled, the entry point takes cares // of poisoning the reference. - codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); - CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); - + codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc()); + CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>(); DCHECK(!codegen_->IsLeafMethod()); } @@ -5416,11 +5436,12 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kJitTableAddress: - break; - case HLoadClass::LoadKind::kDexCachePcRelative: + case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; + case HLoadClass::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; case HLoadClass::LoadKind::kDexCacheViaMethod: break; } @@ -5428,15 +5449,16 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( } void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { - if (cls->NeedsAccessCheck()) { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( + CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(RAX), - /* code_generator_supports_read_barrier */ true); + Location::RegisterLocation(RAX)); return; } + DCHECK(!cls->NeedsAccessCheck()); const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage(); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier) @@ -5447,9 +5469,7 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - HLoadClass::LoadKind load_kind = cls->GetLoadKind(); - if (load_kind == HLoadClass::LoadKind::kReferrersClass || - load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + if (load_kind == HLoadClass::LoadKind::kReferrersClass) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); @@ -5457,23 +5477,26 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite( + TypeReference(&dex_file, dex_index), reinterpret_cast64<uint64_t>(handle.GetReference())); // Add a patch entry and return the label. jit_class_patches_.emplace_back(dex_file, dex_index.index_); PatchInfo<Label>* info = &jit_class_patches_.back(); return &info->label; } -void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { - LocationSummary* locations = cls->GetLocations(); - if (cls->NeedsAccessCheck()) { - codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_); - codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc()); - CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + codegen_->GenerateLoadClassRuntimeCall(cls); return; } + DCHECK(!cls->NeedsAccessCheck()); + LocationSummary* locations = cls->GetLocations(); Location out_loc = locations->Out(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); @@ -5481,7 +5504,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - switch (cls->GetLoadKind()) { + switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); @@ -5496,52 +5519,36 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); - codegen_->RecordTypePatch(cls); + codegen_->RecordBootTypePatch(cls); break; case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ movl(out, Immediate(address)); // Zero-extended. codegen_->RecordSimplePatch(); break; } - case HLoadClass::LoadKind::kJitTableAddress: { - Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, - /* no_rip */ true); - Label* fixup_label = - codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress()); - // /* GcRoot<mirror::Class> */ out = *address - GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); - break; - } - case HLoadClass::LoadKind::kDexCachePcRelative: { - uint32_t offset = cls->GetDexCacheElementOffset(); - Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset); + case HLoadClass::LoadKind::kBssEntry: { Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false); + Label* fixup_label = codegen_->NewTypeBssEntryPatch(cls); // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */ GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + generate_null_check = true; break; } - case HLoadClass::LoadKind::kDexCacheViaMethod: { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>(); - __ movq(out, - Address(current_method, - ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value())); - // /* GcRoot<mirror::Class> */ out = out[type_index] - GenerateGcRootFieldLoad( - cls, - out_loc, - Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)), - /* fixup_label */ nullptr, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + case HLoadClass::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, + /* no_rip */ true); + Label* fixup_label = + codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass()); + // /* GcRoot<mirror::Class> */ out = *address + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); break; } default: @@ -5599,11 +5606,11 @@ HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind( case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; - case HLoadString::LoadKind::kDexCacheViaMethod: - break; case HLoadString::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); break; + case HLoadString::LoadKind::kDexCacheViaMethod: + break; } return desired_string_load_kind; } @@ -5649,6 +5656,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) NO_THREA switch (load->GetLoadKind()) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); codegen_->RecordBootStringPatch(load); return; // No dex cache slow path. diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 32d006c5f3..3438b8159f 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -409,13 +409,16 @@ class CodeGeneratorX86_64 : public CodeGenerator { void RecordSimplePatch(); void RecordBootStringPatch(HLoadString* load_string); - void RecordTypePatch(HLoadClass* load_class); + 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* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index, Handle<mirror::String> handle); - Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address); + Label* NewJitRootClassPatch(const DexFile& dex_file, + dex::TypeIndex dex_index, + Handle<mirror::Class> handle); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; @@ -604,8 +607,10 @@ class CodeGeneratorX86_64 : public CodeGenerator { ArenaDeque<Label> simple_patches_; // String patch locations; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PatchInfo<Label>> string_patches_; - // Type patch locations. - ArenaDeque<PatchInfo<Label>> type_patches_; + // Type patch locations for boot image (always PIC). + ArenaDeque<PatchInfo<Label>> boot_image_type_patches_; + // Type patch locations for kBssEntry. + ArenaDeque<PatchInfo<Label>> type_bss_entry_patches_; // Fixups for jump tables need to be handled specially. ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc index 10a36c6ff4..9ddcd563ca 100644 --- a/compiler/optimizing/dex_cache_array_fixups_arm.cc +++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc @@ -59,29 +59,15 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { } private: - void VisitLoadClass(HLoadClass* load_class) OVERRIDE { - // If this is a load with PC-relative access to the dex cache types array, - // we need to add the dex cache arrays base as the special input. - if (load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCachePcRelative) { - // Initialize base for target dex file if needed. - const DexFile& dex_file = load_class->GetDexFile(); - HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file); - // Update the element offset in base. - DexCacheArraysLayout layout(kArmPointerSize, &dex_file); - base->UpdateElementOffset(layout.TypeOffset(load_class->GetTypeIndex())); - // Add the special argument base to the load. - load_class->AddSpecialInput(base); - } - } - void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { // If this is an invoke with PC-relative access to the dex cache methods array, // we need to add the dex cache arrays base as the special input. if (invoke->HasPcRelativeDexCache() && !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) { - HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile()); + HArmDexCacheArraysBase* base = + GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache()); // Update the element offset in base. - DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile()); + DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFileForPcRelativeDexCache()); base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex())); // Add the special argument base to the method. DCHECK(!invoke->HasCurrentMethodInput()); diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc index 31fff26dd5..04a4294c48 100644 --- a/compiler/optimizing/dex_cache_array_fixups_mips.cc +++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc @@ -53,30 +53,16 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { } private: - void VisitLoadClass(HLoadClass* load_class) OVERRIDE { - // If this is a load with PC-relative access to the dex cache types array, - // we need to add the dex cache arrays base as the special input. - if (load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCachePcRelative) { - // Initialize base for target dex file if needed. - const DexFile& dex_file = load_class->GetDexFile(); - HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file); - // Update the element offset in base. - DexCacheArraysLayout layout(kMipsPointerSize, &dex_file); - base->UpdateElementOffset(layout.TypeOffset(load_class->GetTypeIndex())); - // Add the special argument base to the load. - load_class->AddSpecialInput(base); - } - } - void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { // If this is an invoke with PC-relative access to the dex cache methods array, // we need to add the dex cache arrays base as the special input. if (invoke->HasPcRelativeDexCache() && !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) { // Initialize base for target method dex file if needed. - HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile()); + HMipsDexCacheArraysBase* base = + GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache()); // Update the element offset in base. - DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFile()); + DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFileForPcRelativeDexCache()); base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex())); // Add the special argument base to the method. DCHECK(!invoke->HasCurrentMethodInput()); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 09dcefa02c..f6fba883bd 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -464,6 +464,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("intrinsic") << invoke->GetIntrinsic(); } + void VisitInvokePolymorphic(HInvokePolymorphic* invoke) OVERRIDE { + VisitInvoke(invoke); + StartAttributeStream("invoke_type") << "InvokePolymorphic"; + } + void VisitInstanceFieldGet(HInstanceFieldGet* iget) OVERRIDE { StartAttributeStream("field_name") << iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(), diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index d5c4c2fa69..3973985338 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -114,12 +114,7 @@ static bool IsMaxAtHint( } } else { *suitable = instruction; - while (instruction->IsArrayLength() || - instruction->IsNullCheck() || - instruction->IsNewArray()) { - instruction = instruction->InputAt(0); - } - return instruction == hint; + return HuntForDeclaration(instruction) == hint; } return false; } @@ -368,10 +363,14 @@ void InductionVarRange::Replace(HInstruction* instruction, } } -bool InductionVarRange::IsFinite(HLoopInformation* loop) const { +bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const { HInductionVarAnalysis::InductionInfo *trip = induction_analysis_->LookupInfo(loop, GetLoopControl(loop)); - return trip != nullptr && !IsUnsafeTripCount(trip); + if (trip != nullptr && !IsUnsafeTripCount(trip)) { + IsConstant(trip->op_a, kExact, tc); + return true; + } + return false; } // @@ -625,7 +624,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, if (chase_hint_ == nullptr) { return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max()); } else if (instruction->InputAt(0)->IsNewArray()) { - return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min); + return GetFetch(instruction->InputAt(0)->AsNewArray()->GetLength(), trip, in_body, is_min); } } else if (instruction->IsTypeConversion()) { // Since analysis is 32-bit (or narrower), chase beyond widening along the path. diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index ba14847d82..6c424b78b9 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -150,9 +150,9 @@ class InductionVarRange { } /** - * Checks if header logic of a loop terminates. + * Checks if header logic of a loop terminates. Sets trip-count tc if known. */ - bool IsFinite(HLoopInformation* loop) const; + bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const; private: /* diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index aa3e1aab4f..d81817fb09 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -697,13 +697,8 @@ TEST_F(InductionVarRangeTest, MaxValue) { } TEST_F(InductionVarRangeTest, ArrayLengthAndHints) { - HInstruction* new_array = new (&allocator_) - HNewArray(x_, - graph_->GetCurrentMethod(), - 0, - dex::TypeIndex(Primitive::kPrimInt), - graph_->GetDexFile(), - kQuickAllocArray); + // We pass a bogus constant for the class to avoid mocking one. + HInstruction* new_array = new (&allocator_) HNewArray(x_, x_, 0); entry_block_->AddInstruction(new_array); HInstruction* array_length = new (&allocator_) HArrayLength(new_array, 0); entry_block_->AddInstruction(array_length); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index c970e5cbba..5d40f75618 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -308,8 +308,10 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { } bool HInliner::TryInline(HInvoke* invoke_instruction) { - if (invoke_instruction->IsInvokeUnresolved()) { - return false; // Don't bother to move further if we know the method is unresolved. + if (invoke_instruction->IsInvokeUnresolved() || + invoke_instruction->IsInvokePolymorphic()) { + return false; // Don't bother to move further if we know the method is unresolved or an + // invoke-polymorphic. } ScopedObjectAccess soa(Thread::Current()); @@ -472,10 +474,10 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, HInstruction* receiver = invoke_instruction->InputAt(0); HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - Handle<mirror::Class> handle = handles_->NewHandle(GetMonomorphicType(classes)); + Handle<mirror::Class> monomorphic_type = handles_->NewHandle(GetMonomorphicType(classes)); if (!TryInlineAndReplace(invoke_instruction, resolved_method, - ReferenceTypeInfo::Create(handle, /* is_exact */ true), + ReferenceTypeInfo::Create(monomorphic_type, /* is_exact */ true), /* do_rtp */ false, /* cha_devirtualize */ false)) { return false; @@ -486,7 +488,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, cursor, bb_cursor, class_index, - GetMonomorphicType(classes), + monomorphic_type, invoke_instruction, /* with_deoptimization */ true); @@ -531,11 +533,9 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, dex::TypeIndex class_index, - mirror::Class* klass, + Handle<mirror::Class> klass, HInstruction* invoke_instruction, bool with_deoptimization) { - ScopedAssertNoThreadSuspension sants("Adding compiler type guard"); - ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); HInstanceFieldGet* receiver_class = BuildGetReceiverClass( class_linker, receiver, invoke_instruction->GetDexPc()); @@ -546,19 +546,20 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, } const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); - bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass()); + bool is_referrer = (klass.Get() == outermost_graph_->GetArtMethod()->GetDeclaringClass()); // Note that we will just compare the classes, so we don't need Java semantics access checks. // Note that the type index and the dex file are relative to the method this type guard is // inlined into. HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(), class_index, caller_dex_file, + klass, is_referrer, invoke_instruction->GetDexPc(), /* needs_access_check */ false); bb_cursor->InsertInstructionAfter(load_class, receiver_class); // Sharpen after adding the instruction, as the sharpening may remove inputs. - HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_); + HSharpening::SharpenClass(load_class, codegen_, compiler_driver_); // TODO: Extend reference type propagation to understand the guard. HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class); @@ -635,7 +636,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, cursor, bb_cursor, class_index, - handle.Get(), + handle, invoke_instruction, deoptimize); if (deoptimize) { @@ -1428,15 +1429,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, return false; } - if (current->IsNewArray() && - (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) { - VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) - << " could not be inlined because it is using an entrypoint" - << " with access checks"; - // Allocation entrypoint does not handle inlined frames. - return false; - } - if (current->IsUnresolvedStaticFieldGet() || current->IsUnresolvedInstanceFieldGet() || current->IsUnresolvedStaticFieldSet() || @@ -1537,8 +1529,6 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* } } - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - // Iterate over the list of parameter types and test whether any of the // actual inputs has a more specific reference type than the type declared in // the signature. @@ -1550,9 +1540,9 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* ++param_idx, ++input_idx) { HInstruction* input = invoke_instruction->InputAt(input_idx); if (input->GetType() == Primitive::kPrimNot) { - mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType( + mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex( param_list->GetTypeItem(param_idx).type_idx_, - pointer_size); + /* resolve */ false); if (IsReferenceTypeRefinement(GetClassRTI(param_cls), /* declared_can_be_null */ true, input)) { @@ -1601,8 +1591,7 @@ void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, // TODO: we could be more precise by merging the phi inputs but that requires // some functionality from the reference type propagation. DCHECK(return_replacement->IsPhi()); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); + mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */); return_replacement->SetReferenceTypeInfo(GetClassRTI(cls)); } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 4c0b990f26..11aacab802 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -170,7 +170,7 @@ class HInliner : public HOptimization { HInstruction* cursor, HBasicBlock* bb_cursor, dex::TypeIndex class_index, - mirror::Class* klass, + Handle<mirror::Class> klass, HInstruction* invoke_instruction, bool with_deoptimization) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 009d549547..ef8d74dce0 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1,3 +1,4 @@ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -207,10 +208,8 @@ void HInstructionBuilder::InitializeInstruction(HInstruction* instruction) { HEnvironment* environment = new (arena_) HEnvironment( arena_, current_locals_->size(), - graph_->GetDexFile(), - graph_->GetMethodIdx(), + graph_->GetArtMethod(), instruction->GetDexPc(), - graph_->GetInvokeType(), instruction); environment->CopyFrom(*current_locals_); instruction->SetRawEnvironment(environment); @@ -906,50 +905,69 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, false /* is_unresolved */); } +bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, + uint32_t dex_pc, + uint32_t method_idx, + uint32_t proto_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + const char* descriptor = dex_file_->GetShorty(proto_idx); + DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), number_of_vreg_arguments); + Primitive::Type return_type = Primitive::GetType(descriptor[0]); + size_t number_of_arguments = strlen(descriptor); + HInvoke* invoke = new (arena_) HInvokePolymorphic(arena_, + number_of_arguments, + return_type, + dex_pc, + method_idx); + return HandleInvoke(invoke, + number_of_vreg_arguments, + args, + register_index, + is_range, + descriptor, + nullptr /* clinit_check */, + false /* is_unresolved */); +} + bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); - bool finalizable; - bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable); - - // Only the access check entrypoint handles the finalizable class case. If we - // need access checks, then we haven't resolved the method and the class may - // again be finalizable. - QuickEntrypointEnum entrypoint = (finalizable || needs_access_check) - ? kQuickAllocObjectWithChecks - : kQuickAllocObjectInitialized; - if (outer_dex_cache.Get() != dex_cache.Get()) { // We currently do not support inlining allocations across dex files. return false; } - HLoadClass* load_class = new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - type_index, - outer_dex_file, - IsOutermostCompilingClass(type_index), - dex_pc, - needs_access_check); + HLoadClass* load_class = BuildLoadClass(type_index, dex_pc, /* check_access */ true); - AppendInstruction(load_class); HInstruction* cls = load_class; - if (!IsInitialized(resolved_class)) { + Handle<mirror::Class> klass = load_class->GetClass(); + + if (!IsInitialized(klass)) { cls = new (arena_) HClinitCheck(load_class, dex_pc); AppendInstruction(cls); } + // Only the access check entrypoint handles the finalizable class case. If we + // need access checks, then we haven't resolved the method and the class may + // again be finalizable. + QuickEntrypointEnum entrypoint = kQuickAllocObjectInitialized; + if (load_class->NeedsAccessCheck() || klass->IsFinalizable() || !klass->IsInstantiable()) { + entrypoint = kQuickAllocObjectWithChecks; + } + + // Consider classes we haven't resolved as potentially finalizable. + bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable(); + AppendInstruction(new (arena_) HNewInstance( cls, dex_pc, type_index, *dex_compilation_unit_->GetDexFile(), - needs_access_check, finalizable, entrypoint)); return true; @@ -990,7 +1008,6 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( ArtMethod* resolved_method, uint32_t method_idx, HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Thread* self = Thread::Current(); StackHandleScope<2> hs(self); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); @@ -1018,15 +1035,9 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; } else if (storage_index.IsValid()) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; - HLoadClass* load_class = new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - storage_index, - outer_dex_file, - is_outer_class, - dex_pc, - /*needs_access_check*/ false); - AppendInstruction(load_class); - clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); + HLoadClass* cls = BuildLoadClass( + storage_index, dex_pc, /* check_access */ false, /* outer */ true); + clinit_check = new (arena_) HClinitCheck(cls, dex_pc); AppendInstruction(clinit_check); } return clinit_check; @@ -1348,7 +1359,6 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); @@ -1376,16 +1386,10 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } } - HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(), - storage_index, - outer_dex_file, - is_outer_class, - dex_pc, - /*needs_access_check*/ false); - AppendInstruction(constant); + HLoadClass* constant = BuildLoadClass( + storage_index, dex_pc, /* check_access */ false, /* outer */ true); HInstruction* cls = constant; - Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass())); if (!IsInitialized(klass)) { cls = new (arena_) HClinitCheck(constant, dex_pc); @@ -1494,16 +1498,8 @@ void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, uint32_t* args, uint32_t register_index) { HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc); - bool finalizable; - QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable) - ? kQuickAllocArrayWithAccessCheck - : kQuickAllocArray; - HInstruction* object = new (arena_) HNewArray(length, - graph_->GetCurrentMethod(), - dex_pc, - type_index, - *dex_compilation_unit_->GetDexFile(), - entrypoint); + HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true); + HInstruction* object = new (arena_) HNewArray(cls, length, dex_pc); AppendInstruction(object); const char* descriptor = dex_file_->StringByTypeIdx(type_index); @@ -1632,33 +1628,57 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) } } -void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, - uint8_t destination, - uint8_t reference, - dex::TypeIndex type_index, - uint32_t dex_pc) { +HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, + uint32_t dex_pc, + bool check_access, + bool outer) { ScopedObjectAccess soa(Thread::Current()); + const DexCompilationUnit* compilation_unit = + outer ? outer_compilation_unit_ : dex_compilation_unit_; + const DexFile& dex_file = *compilation_unit->GetDexFile(); StackHandleScope<1> hs(soa.Self()); - const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - - bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), - dex_cache, - type_index); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass( + soa, compilation_unit->GetDexCache(), class_loader, type_index, compilation_unit)); + + bool is_accessible = false; + if (!check_access) { + is_accessible = true; + } else if (klass.Get() != nullptr) { + if (klass->IsPublic()) { + is_accessible = true; + } else { + mirror::Class* compiling_class = GetCompilingClass(); + if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { + is_accessible = true; + } + } + } - HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); - HLoadClass* cls = new (arena_) HLoadClass( + HLoadClass* load_class = new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, dex_file, - IsOutermostCompilingClass(type_index), + klass, + klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()), dex_pc, - !can_access); - AppendInstruction(cls); + !is_accessible); + + AppendInstruction(load_class); + return load_class; +} + +void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, + uint8_t destination, + uint8_t reference, + dex::TypeIndex type_index, + uint32_t dex_pc) { + HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); + HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true); - TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class); + ScopedObjectAccess soa(Thread::Current()); + TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); if (instruction.Opcode() == Instruction::INSTANCE_OF) { AppendInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc)); UpdateLocal(destination, current_block_->GetLastInstruction()); @@ -1915,6 +1935,37 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::INVOKE_POLYMORPHIC: { + uint16_t method_idx = instruction.VRegB_45cc(); + uint16_t proto_idx = instruction.VRegH_45cc(); + uint32_t number_of_vreg_arguments = instruction.VRegA_45cc(); + uint32_t args[5]; + instruction.GetVarArgs(args); + return BuildInvokePolymorphic(instruction, + dex_pc, + method_idx, + proto_idx, + number_of_vreg_arguments, + false, + args, + -1); + } + + case Instruction::INVOKE_POLYMORPHIC_RANGE: { + uint16_t method_idx = instruction.VRegB_4rcc(); + uint16_t proto_idx = instruction.VRegH_4rcc(); + uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc(); + uint32_t register_index = instruction.VRegC_4rcc(); + return BuildInvokePolymorphic(instruction, + dex_pc, + method_idx, + proto_idx, + number_of_vreg_arguments, + true, + nullptr, + register_index); + } + case Instruction::NEG_INT: { Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc); break; @@ -2448,16 +2499,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::NEW_ARRAY: { dex::TypeIndex type_index(instruction.VRegC_22c()); HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt); - bool finalizable; - QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable) - ? kQuickAllocArrayWithAccessCheck - : kQuickAllocArray; - AppendInstruction(new (arena_) HNewArray(length, - graph_->GetCurrentMethod(), - dex_pc, - type_index, - *dex_compilation_unit_->GetDexFile(), - entrypoint)); + HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true); + AppendInstruction(new (arena_) HNewArray(cls, length, dex_pc)); UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction()); break; } @@ -2631,21 +2674,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_CLASS: { dex::TypeIndex type_index(instruction.VRegB_21c()); - // `CanAccessTypeWithoutChecks` will tell whether the method being - // built is trying to access its own class, so that the generated - // code can optimize for this case. However, the optimization does not - // work for inlining, so we use `IsOutermostCompilingClass` instead. - ScopedObjectAccess soa(Thread::Current()); - Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index); - AppendInstruction(new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - type_index, - *dex_file_, - IsOutermostCompilingClass(type_index), - dex_pc, - !can_access)); + BuildLoadClass(type_index, dex_pc, /* check_access */ true); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f29e522040..5efe95094c 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -46,9 +46,11 @@ class HInstructionBuilder : public ValueObject { CompilerDriver* driver, const uint8_t* interpreter_metadata, OptimizingCompilerStats* compiler_stats, - Handle<mirror::DexCache> dex_cache) + Handle<mirror::DexCache> dex_cache, + VariableSizedHandleScope* handles) : arena_(graph->GetArena()), graph_(graph), + handles_(handles), dex_file_(dex_file), code_item_(code_item), return_type_(return_type), @@ -175,6 +177,17 @@ class HInstructionBuilder : public ValueObject { uint32_t* args, uint32_t register_index); + // Builds an invocation node for invoke-polymorphic and returns whether the + // instruction is supported. + bool BuildInvokePolymorphic(const Instruction& instruction, + uint32_t dex_pc, + uint32_t method_idx, + uint32_t proto_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index); + // Builds a new array node and the instructions that fill it. void BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, @@ -212,6 +225,14 @@ class HInstructionBuilder : public ValueObject { // Builds an instruction sequence for a switch statement. void BuildSwitch(const Instruction& instruction, uint32_t dex_pc); + // Builds a `HLoadClass` loading the given `type_index`. If `outer` is true, + // this method will use the outer class's dex file to lookup the type at + // `type_index`. + HLoadClass* BuildLoadClass(dex::TypeIndex type_index, + uint32_t dex_pc, + bool check_access, + bool outer = false); + // Returns the outer-most compiling method's class. mirror::Class* GetOutermostCompilingClass() const; @@ -271,6 +292,7 @@ class HInstructionBuilder : public ValueObject { ArenaAllocator* const arena_; HGraph* const graph_; + VariableSizedHandleScope* handles_; // The dex file where the method being compiled is, and the bytecode data. const DexFile* const dex_file_; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 911bfb9cc6..35f59cb4a4 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -777,7 +777,7 @@ void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) { // If the array is a NewArray with constant size, replace the array length // with the constant instruction. This helps the bounds check elimination phase. if (input->IsNewArray()) { - input = input->InputAt(0); + input = input->AsNewArray()->GetLength(); if (input->IsIntConstant()) { instruction->ReplaceWith(input); } @@ -1774,7 +1774,7 @@ static bool IsArrayLengthOf(HInstruction* potential_length, HInstruction* potent } if (potential_array->IsNewArray()) { - return potential_array->InputAt(0) == potential_length; + return potential_array->AsNewArray()->GetLength() == potential_length; } return false; diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index fc6ff7b197..17d683f357 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -145,7 +145,7 @@ void IntrinsicsRecognizer::Run() { if (!CheckInvokeType(intrinsic, invoke)) { LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " << intrinsic << " for " - << invoke->GetDexFile().PrettyMethod(invoke->GetDexMethodIndex()) + << art_method->PrettyMethod() << invoke->DebugName(); } else { invoke->SetIntrinsic(intrinsic, diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 2856c3ea11..2d3c00fb97 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -943,6 +943,10 @@ class LSEVisitor : public HGraphVisitor { HandleInvoke(invoke); } + void VisitInvokePolymorphic(HInvokePolymorphic* invoke) OVERRIDE { + HandleInvoke(invoke); + } + void VisitClinitCheck(HClinitCheck* clinit) OVERRIDE { HandleInvoke(clinit); } @@ -975,7 +979,7 @@ class LSEVisitor : public HGraphVisitor { } if (ref_info->IsSingletonAndRemovable() && !new_instance->IsFinalizable() && - !new_instance->NeedsAccessCheck()) { + !new_instance->NeedsChecks()) { singleton_new_instances_.push_back(new_instance); } ArenaVector<HInstruction*>& heap_values = diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 9d73e29602..95838380cc 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -161,26 +161,27 @@ void HLoopOptimization::RemoveLoop(LoopNode* node) { void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) { for ( ; node != nullptr; node = node->next) { + // Visit inner loops first. int current_induction_simplification_count = induction_simplication_count_; if (node->inner != nullptr) { TraverseLoopsInnerToOuter(node->inner); } - // Visit loop after its inner loops have been visited. If the induction of any inner - // loop has been simplified, recompute the induction information of this loop first. + // Recompute induction information of this loop if the induction + // of any inner loop has been simplified. if (current_induction_simplification_count != induction_simplication_count_) { induction_range_.ReVisit(node->loop_info); } - // Repeat simplifications until no more changes occur. Note that since - // each simplification consists of eliminating code (without introducing - // new code), this process is always finite. + // Repeat simplifications in the body of this loop until no more changes occur. + // Note that since each simplification consists of eliminating code (without + // introducing new code), this process is always finite. do { simplified_ = false; - SimplifyBlocks(node); SimplifyInduction(node); + SimplifyBlocks(node); } while (simplified_); - // Remove inner loops when empty. + // Simplify inner loop. if (node->inner == nullptr) { - RemoveIfEmptyInnerLoop(node); + SimplifyInnerLoop(node); } } } @@ -198,7 +199,7 @@ void HLoopOptimization::SimplifyInduction(LoopNode* node) { iset_->clear(); int32_t use_count = 0; if (IsPhiInduction(phi) && - IsOnlyUsedAfterLoop(node->loop_info, phi, &use_count) && + IsOnlyUsedAfterLoop(node->loop_info, phi, /*collect_loop_uses*/ false, &use_count) && // No uses, or no early-exit with proper replacement. (use_count == 0 || (!IsEarlyExit(node->loop_info) && TryReplaceWithLastValue(phi, preheader)))) { @@ -206,7 +207,6 @@ void HLoopOptimization::SimplifyInduction(LoopNode* node) { RemoveFromCycle(i); } simplified_ = true; - induction_simplication_count_++; } } } @@ -216,24 +216,14 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) { for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); // Remove dead instructions from the loop-body. - for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) { - HInstruction* instruction = i.Current(); - if (instruction->IsDeadAndRemovable()) { - simplified_ = true; - block->RemoveInstruction(instruction); - } - } + RemoveDeadInstructions(block->GetPhis()); + RemoveDeadInstructions(block->GetInstructions()); // Remove trivial control flow blocks from the loop-body. - HBasicBlock* succ = nullptr; - if (IsGotoBlock(block, &succ) && succ->GetPredecessors().size() == 1) { - // Trivial goto block can be removed. - HBasicBlock* pred = block->GetSinglePredecessor(); + if (block->GetPredecessors().size() == 1 && + block->GetSuccessors().size() == 1 && + block->GetSingleSuccessor()->GetPredecessors().size() == 1) { simplified_ = true; - pred->ReplaceSuccessor(block, succ); - block->RemoveDominatedBlock(succ); - block->DisconnectAndDelete(); - pred->AddDominatedBlock(succ); - succ->SetDominator(pred); + block->MergeWith(block->GetSingleSuccessor()); } else if (block->GetSuccessors().size() == 2) { // Trivial if block can be bypassed to either branch. HBasicBlock* succ0 = block->GetSuccessors()[0]; @@ -258,55 +248,66 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) { } } -void HLoopOptimization::RemoveIfEmptyInnerLoop(LoopNode* node) { +bool HLoopOptimization::SimplifyInnerLoop(LoopNode* node) { HBasicBlock* header = node->loop_info->GetHeader(); HBasicBlock* preheader = node->loop_info->GetPreHeader(); // Ensure loop header logic is finite. - if (!induction_range_.IsFinite(node->loop_info)) { - return; + int64_t tc = 0; + if (!induction_range_.IsFinite(node->loop_info, &tc)) { + return false; } // Ensure there is only a single loop-body (besides the header). HBasicBlock* body = nullptr; for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) { if (it.Current() != header) { if (body != nullptr) { - return; + return false; } body = it.Current(); } } // Ensure there is only a single exit point. if (header->GetSuccessors().size() != 2) { - return; + return false; } HBasicBlock* exit = (header->GetSuccessors()[0] == body) ? header->GetSuccessors()[1] : header->GetSuccessors()[0]; // Ensure exit can only be reached by exiting loop. if (exit->GetPredecessors().size() != 1) { - return; + return false; } - // Detect an empty loop: no side effects other than plain iteration. Replace - // subsequent index uses, if any, with the last value and remove the loop. + // Detect either an empty loop (no side effects other than plain iteration) or + // a trivial loop (just iterating once). Replace subsequent index uses, if any, + // with the last value and remove the loop, possibly after unrolling its body. + HInstruction* phi = header->GetFirstPhi(); iset_->clear(); int32_t use_count = 0; - if (IsEmptyHeader(header) && - IsEmptyBody(body) && - IsOnlyUsedAfterLoop(node->loop_info, header->GetFirstPhi(), &use_count) && - // No uses, or proper replacement. - (use_count == 0 || TryReplaceWithLastValue(header->GetFirstPhi(), preheader))) { - body->DisconnectAndDelete(); - exit->RemovePredecessor(header); - header->RemoveSuccessor(exit); - header->RemoveDominatedBlock(exit); - header->DisconnectAndDelete(); - preheader->AddSuccessor(exit); - preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator - preheader->AddDominatedBlock(exit); - exit->SetDominator(preheader); - // Update hierarchy. - RemoveLoop(node); + if (IsEmptyHeader(header)) { + bool is_empty = IsEmptyBody(body); + if ((is_empty || tc == 1) && + IsOnlyUsedAfterLoop(node->loop_info, phi, /*collect_loop_uses*/ true, &use_count) && + // No uses, or proper replacement. + (use_count == 0 || TryReplaceWithLastValue(phi, preheader))) { + if (!is_empty) { + // Unroll the loop body, which sees initial value of the index. + phi->ReplaceWith(phi->InputAt(0)); + preheader->MergeInstructionsWith(body); + } + body->DisconnectAndDelete(); + exit->RemovePredecessor(header); + header->RemoveSuccessor(exit); + header->RemoveDominatedBlock(exit); + header->DisconnectAndDelete(); + preheader->AddSuccessor(exit); + preheader->AddInstruction(new (graph_->GetArena()) HGoto()); // global allocator + preheader->AddDominatedBlock(exit); + exit->SetDominator(preheader); + RemoveLoop(node); // update hierarchy + return true; + } } + return false; } bool HLoopOptimization::IsPhiInduction(HPhi* phi) { @@ -374,12 +375,19 @@ bool HLoopOptimization::IsEmptyBody(HBasicBlock* block) { bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info, HInstruction* instruction, + bool collect_loop_uses, /*out*/ int32_t* use_count) { for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) { HInstruction* user = use.GetUser(); if (iset_->find(user) == iset_->end()) { // not excluded? HLoopInformation* other_loop_info = user->GetBlock()->GetLoopInformation(); if (other_loop_info != nullptr && other_loop_info->IsIn(*loop_info)) { + // If collect_loop_uses is set, simply keep adding those uses to the set. + // Otherwise, reject uses inside the loop that were not already in the set. + if (collect_loop_uses) { + iset_->insert(user); + continue; + } return false; } ++*use_count; @@ -388,40 +396,48 @@ bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info, return true; } -void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, HInstruction* replacement) { - const HUseList<HInstruction*>& uses = instruction->GetUses(); - for (auto it = uses.begin(), end = uses.end(); it != end;) { - HInstruction* user = it->GetUser(); - size_t index = it->GetIndex(); - ++it; // increment before replacing - if (iset_->find(user) == iset_->end()) { // not excluded? - user->ReplaceInput(replacement, index); - induction_range_.Replace(user, instruction, replacement); // update induction - } - } - const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses(); - for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) { - HEnvironment* user = it->GetUser(); - size_t index = it->GetIndex(); - ++it; // increment before replacing - if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded? - user->RemoveAsUserOfInput(index); - user->SetRawEnvAt(index, replacement); - replacement->AddEnvUseAt(user, index); - } - } -} - bool HLoopOptimization::TryReplaceWithLastValue(HInstruction* instruction, HBasicBlock* block) { // Try to replace outside uses with the last value. Environment uses can consume this // value too, since any first true use is outside the loop (although this may imply // that de-opting may look "ahead" a bit on the phi value). If there are only environment // uses, the value is dropped altogether, since the computations have no effect. if (induction_range_.CanGenerateLastValue(instruction)) { - ReplaceAllUses(instruction, induction_range_.GenerateLastValue(instruction, graph_, block)); + HInstruction* replacement = induction_range_.GenerateLastValue(instruction, graph_, block); + const HUseList<HInstruction*>& uses = instruction->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end;) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + ++it; // increment before replacing + if (iset_->find(user) == iset_->end()) { // not excluded? + user->ReplaceInput(replacement, index); + induction_range_.Replace(user, instruction, replacement); // update induction + } + } + const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses(); + for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) { + HEnvironment* user = it->GetUser(); + size_t index = it->GetIndex(); + ++it; // increment before replacing + if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded? + user->RemoveAsUserOfInput(index); + user->SetRawEnvAt(index, replacement); + replacement->AddEnvUseAt(user, index); + } + } + induction_simplication_count_++; return true; } return false; } +void HLoopOptimization::RemoveDeadInstructions(const HInstructionList& list) { + for (HBackwardInstructionIterator i(list); !i.Done(); i.Advance()) { + HInstruction* instruction = i.Current(); + if (instruction->IsDeadAndRemovable()) { + simplified_ = true; + instruction->GetBlock()->RemoveInstructionOrPhi(instruction); + } + } +} + } // namespace art diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 0f05b24c37..9ddab4150c 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -60,19 +60,21 @@ class HLoopOptimization : public HOptimization { void TraverseLoopsInnerToOuter(LoopNode* node); + // Simplification. void SimplifyInduction(LoopNode* node); void SimplifyBlocks(LoopNode* node); - void RemoveIfEmptyInnerLoop(LoopNode* node); + bool SimplifyInnerLoop(LoopNode* node); + // Helpers. bool IsPhiInduction(HPhi* phi); bool IsEmptyHeader(HBasicBlock* block); bool IsEmptyBody(HBasicBlock* block); - bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info, HInstruction* instruction, + bool collect_loop_uses, /*out*/ int32_t* use_count); - void ReplaceAllUses(HInstruction* instruction, HInstruction* replacement); bool TryReplaceWithLastValue(HInstruction* instruction, HBasicBlock* block); + void RemoveDeadInstructions(const HInstructionList& list); // Range information based on prior induction variable analysis. InductionVarRange induction_range_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d45fa11534..d15145e673 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1853,6 +1853,14 @@ void HBasicBlock::DisconnectAndDelete() { SetGraph(nullptr); } +void HBasicBlock::MergeInstructionsWith(HBasicBlock* other) { + DCHECK(EndsWithControlFlowInstruction()); + RemoveInstruction(GetLastInstruction()); + instructions_.Add(other->GetInstructions()); + other->instructions_.SetBlockOfInstructions(this); + other->instructions_.Clear(); +} + void HBasicBlock::MergeWith(HBasicBlock* other) { DCHECK_EQ(GetGraph(), other->GetGraph()); DCHECK(ContainsElement(dominated_blocks_, other)); @@ -1861,11 +1869,7 @@ void HBasicBlock::MergeWith(HBasicBlock* other) { DCHECK(other->GetPhis().IsEmpty()); // Move instructions from `other` to `this`. - DCHECK(EndsWithControlFlowInstruction()); - RemoveInstruction(GetLastInstruction()); - instructions_.Add(other->GetInstructions()); - other->instructions_.SetBlockOfInstructions(this); - other->instructions_.Clear(); + MergeInstructionsWith(other); // Remove `other` from the loops it is included in. for (HLoopInformationOutwardIterator it(*other); !it.Done(); it.Advance()) { @@ -2387,6 +2391,14 @@ bool HInvoke::NeedsEnvironment() const { return !opt.GetDoesNotNeedEnvironment(); } +const DexFile& HInvokeStaticOrDirect::GetDexFileForPcRelativeDexCache() const { + ArtMethod* caller = GetEnvironment()->GetMethod(); + ScopedObjectAccess soa(Thread::Current()); + // `caller` is null for a top-level graph representing a method whose declaring + // class was not resolved. + return caller == nullptr ? GetBlock()->GetGraph()->GetDexFile() : *caller->GetDexFile(); +} + bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const { if (GetMethodLoadKind() != MethodLoadKind::kDexCacheViaMethod) { return false; @@ -2430,17 +2442,6 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq } } -// Helper for InstructionDataEquals to fetch the mirror Class out -// from a kJitTableAddress LoadClass kind. -// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing -// mirrors, they are stored in a variable size handle scope which is always -// visited during a pause. Also, the only caller of this helper -// only uses the mirror for pointer comparison. -static inline mirror::Class* AsMirrorInternal(uint64_t address) - NO_THREAD_SAFETY_ANALYSIS { - return reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr(); -} - bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { const HLoadClass* other_load_class = other->AsLoadClass(); // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type @@ -2451,11 +2452,12 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - return GetAddress() == other_load_class->GetAddress(); - case LoadKind::kJitTableAddress: - return AsMirrorInternal(GetAddress()) == AsMirrorInternal(other_load_class->GetAddress()); + case LoadKind::kJitTableAddress: { + ScopedObjectAccess soa(Thread::Current()); + return GetClass().Get() == other_load_class->GetClass().Get(); + } default: - DCHECK(HasTypeReference(GetLoadKind()) || HasDexCacheReference(GetLoadKind())); + DCHECK(HasTypeReference(GetLoadKind())); return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); } } @@ -2486,10 +2488,10 @@ std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { return os << "BootImageLinkTimePcRelative"; case HLoadClass::LoadKind::kBootImageAddress: return os << "BootImageAddress"; + case HLoadClass::LoadKind::kBssEntry: + return os << "BssEntry"; case HLoadClass::LoadKind::kJitTableAddress: return os << "JitTableAddress"; - case HLoadClass::LoadKind::kDexCachePcRelative: - return os << "DexCachePcRelative"; case HLoadClass::LoadKind::kDexCacheViaMethod: return os << "DexCacheViaMethod"; default: @@ -2498,17 +2500,6 @@ std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { } } -// Helper for InstructionDataEquals to fetch the mirror String out -// from a kJitTableAddress LoadString kind. -// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing -// mirrors, they are stored in a variable size handle scope which is always -// visited during a pause. Also, the only caller of this helper -// only uses the mirror for pointer comparison. -static inline mirror::String* AsMirrorInternal(Handle<mirror::String> handle) - NO_THREAD_SAFETY_ANALYSIS { - return handle.Get(); -} - bool HLoadString::InstructionDataEquals(const HInstruction* other) const { const HLoadString* other_load_string = other->AsLoadString(); // TODO: To allow GVN for HLoadString from different dex files, we should compare the strings @@ -2519,8 +2510,10 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - case LoadKind::kJitTableAddress: - return AsMirrorInternal(GetString()) == AsMirrorInternal(other_load_string->GetString()); + case LoadKind::kJitTableAddress: { + ScopedObjectAccess soa(Thread::Current()); + return GetString().Get() == other_load_string->GetString().Get(); + } default: return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile()); } @@ -2551,10 +2544,10 @@ std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) { return os << "BootImageAddress"; case HLoadString::LoadKind::kBssEntry: return os << "BssEntry"; - case HLoadString::LoadKind::kDexCacheViaMethod: - return os << "DexCacheViaMethod"; case HLoadString::LoadKind::kJitTableAddress: return os << "JitTableAddress"; + case HLoadString::LoadKind::kDexCacheViaMethod: + return os << "DexCacheViaMethod"; default: LOG(FATAL) << "Unknown HLoadString::LoadKind: " << static_cast<int>(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ea9a94c420..a2980dca20 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1097,6 +1097,9 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { // with a control flow instruction). void ReplaceWith(HBasicBlock* other); + // Merges the instructions of `other` at the end of `this`. + void MergeInstructionsWith(HBasicBlock* other); + // Merge `other` at the end of `this`. This method updates loops, reverse post // order, links to predecessors, successors, dominators and deletes the block // from the graph. The two blocks must be successive, i.e. `this` the only @@ -1291,6 +1294,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(InvokeInterface, Invoke) \ M(InvokeStaticOrDirect, Invoke) \ M(InvokeVirtual, Invoke) \ + M(InvokePolymorphic, Invoke) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ @@ -1720,28 +1724,22 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { public: HEnvironment(ArenaAllocator* arena, size_t number_of_vregs, - const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* method, uint32_t dex_pc, - InvokeType invoke_type, HInstruction* holder) : vregs_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentVRegs)), locations_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentLocations)), parent_(nullptr), - dex_file_(dex_file), - method_idx_(method_idx), + method_(method), dex_pc_(dex_pc), - invoke_type_(invoke_type), holder_(holder) { } HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy, HInstruction* holder) : HEnvironment(arena, to_copy.Size(), - to_copy.GetDexFile(), - to_copy.GetMethodIdx(), + to_copy.GetMethod(), to_copy.GetDexPc(), - to_copy.GetInvokeType(), holder) {} void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) { @@ -1790,16 +1788,8 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { return dex_pc_; } - uint32_t GetMethodIdx() const { - return method_idx_; - } - - InvokeType GetInvokeType() const { - return invoke_type_; - } - - const DexFile& GetDexFile() const { - return dex_file_; + ArtMethod* GetMethod() const { + return method_; } HInstruction* GetHolder() const { @@ -1815,10 +1805,8 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { ArenaVector<HUserRecord<HEnvironment*>> vregs_; ArenaVector<Location> locations_; HEnvironment* parent_; - const DexFile& dex_file_; - const uint32_t method_idx_; + ArtMethod* method_; const uint32_t dex_pc_; - const InvokeType invoke_type_; // The instruction that holds this environment. HInstruction* const holder_; @@ -3780,14 +3768,12 @@ class HNewInstance FINAL : public HExpression<1> { uint32_t dex_pc, dex::TypeIndex type_index, const DexFile& dex_file, - bool needs_access_check, bool finalizable, QuickEntrypointEnum entrypoint) : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), entrypoint_(entrypoint) { - SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); } @@ -3801,8 +3787,9 @@ class HNewInstance FINAL : public HExpression<1> { // Can throw errors when out-of-memory or if it's not instantiable/accessible. bool CanThrow() const OVERRIDE { return true; } - // Needs to call into runtime to make sure it's instantiable/accessible. - bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } + bool NeedsChecks() const { + return entrypoint_ == kQuickAllocObjectWithChecks; + } bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); } @@ -3814,13 +3801,21 @@ class HNewInstance FINAL : public HExpression<1> { entrypoint_ = entrypoint; } + HLoadClass* GetLoadClass() const { + HInstruction* input = InputAt(0); + if (input->IsClinitCheck()) { + input = input->InputAt(0); + } + DCHECK(input->IsLoadClass()); + return input->AsLoadClass(); + } + bool IsStringAlloc() const; DECLARE_INSTRUCTION(NewInstance); private: - static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits; - static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1; + static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -3866,7 +3861,6 @@ class HInvoke : public HVariableInputSizeInstruction { Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); } uint32_t GetDexMethodIndex() const { return dex_method_index_; } - const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); } InvokeType GetInvokeType() const { return GetPackedField<InvokeTypeField>(); @@ -3983,6 +3977,28 @@ class HInvokeUnresolved FINAL : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved); }; +class HInvokePolymorphic FINAL : public HInvoke { + public: + HInvokePolymorphic(ArenaAllocator* arena, + uint32_t number_of_arguments, + Primitive::Type return_type, + uint32_t dex_pc, + uint32_t dex_method_index) + : HInvoke(arena, + number_of_arguments, + 0u /* number_of_other_inputs */, + return_type, + dex_pc, + dex_method_index, + nullptr, + kVirtual) {} + + DECLARE_INSTRUCTION(InvokePolymorphic); + + private: + DISALLOW_COPY_AND_ASSIGN(HInvokePolymorphic); +}; + class HInvokeStaticOrDirect FINAL : public HInvoke { public: // Requirements of this method call regarding the class @@ -4164,6 +4180,8 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { return dispatch_info_.method_load_data; } + const DexFile& GetDexFileForPcRelativeDexCache() const; + ClinitCheckRequirement GetClinitCheckRequirement() const { return GetPackedField<ClinitCheckRequirementField>(); } @@ -4346,23 +4364,12 @@ class HNeg FINAL : public HUnaryOperation { class HNewArray FINAL : public HExpression<2> { public: - HNewArray(HInstruction* length, - HCurrentMethod* current_method, - uint32_t dex_pc, - dex::TypeIndex type_index, - const DexFile& dex_file, - QuickEntrypointEnum entrypoint) - : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), - type_index_(type_index), - dex_file_(dex_file), - entrypoint_(entrypoint) { - SetRawInputAt(0, length); - SetRawInputAt(1, current_method); + HNewArray(HInstruction* cls, HInstruction* length, uint32_t dex_pc) + : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc) { + SetRawInputAt(0, cls); + SetRawInputAt(1, length); } - dex::TypeIndex GetTypeIndex() const { return type_index_; } - const DexFile& GetDexFile() const { return dex_file_; } - // Calls runtime so needs an environment. bool NeedsEnvironment() const OVERRIDE { return true; } @@ -4371,15 +4378,18 @@ class HNewArray FINAL : public HExpression<2> { bool CanBeNull() const OVERRIDE { return false; } - QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; } + HLoadClass* GetLoadClass() const { + DCHECK(InputAt(0)->IsLoadClass()); + return InputAt(0)->AsLoadClass(); + } + + HInstruction* GetLength() const { + return InputAt(1); + } DECLARE_INSTRUCTION(NewArray); private: - const dex::TypeIndex type_index_; - const DexFile& dex_file_; - const QuickEntrypointEnum entrypoint_; - DISALLOW_COPY_AND_ASSIGN(HNewArray); }; @@ -5423,10 +5433,10 @@ class HBoundsCheck FINAL : public HExpression<2> { HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc, - uint32_t string_char_at_method_index = DexFile::kDexNoIndex) - : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc), - string_char_at_method_index_(string_char_at_method_index) { + bool string_char_at = false) + : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType())); + SetPackedFlag<kFlagIsStringCharAt>(string_char_at); SetRawInputAt(0, index); SetRawInputAt(1, length); } @@ -5440,22 +5450,14 @@ class HBoundsCheck FINAL : public HExpression<2> { bool CanThrow() const OVERRIDE { return true; } - bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; } - uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; } + bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); } HInstruction* GetIndex() const { return InputAt(0); } DECLARE_INSTRUCTION(BoundsCheck); private: - // We treat a String as an array, creating the HBoundsCheck from String.charAt() - // intrinsic in the instruction simplifier. We want to include the String.charAt() - // in the stack trace if we actually throw the StringIndexOutOfBoundsException, - // so we need to create an HEnvironment which will be translated to an InlineInfo - // indicating the extra stack frame. Since we add this HEnvironment quite late, - // in the PrepareForRegisterAllocation pass, we need to remember the method index - // from the invoke as we don't want to look again at the dex bytecode. - uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array. + static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; @@ -5523,14 +5525,13 @@ class HLoadClass FINAL : public HInstruction { // GetIncludePatchInformation(). kBootImageAddress, + // Load from an entry in the .bss section using a PC-relative load. + // Used for classes outside boot image when .bss is accessible with a PC-relative load. + kBssEntry, + // Load from the root table associated with the JIT compiled method. kJitTableAddress, - // Load from resolved types array in the dex cache using a PC-relative load. - // Used for classes outside boot image when we know that we can access - // the dex cache arrays using a PC-relative load. - kDexCachePcRelative, - // Load from resolved types array accessed through the class loaded from // the compiled method's own ArtMethod*. This is the default access type when // all other types are unavailable. @@ -5542,6 +5543,7 @@ class HLoadClass FINAL : public HInstruction { HLoadClass(HCurrentMethod* current_method, dex::TypeIndex type_index, const DexFile& dex_file, + Handle<mirror::Class> klass, bool is_referrers_class, uint32_t dex_pc, bool needs_access_check) @@ -5549,6 +5551,7 @@ class HLoadClass FINAL : public HInstruction { special_input_(HUserRecord<HInstruction*>(current_method)), type_index_(type_index), dex_file_(dex_file), + klass_(klass), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. @@ -5557,14 +5560,11 @@ class HLoadClass FINAL : public HInstruction { SetPackedField<LoadKindField>( is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod); SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); - SetPackedFlag<kFlagIsInDexCache>(false); SetPackedFlag<kFlagIsInBootImage>(false); SetPackedFlag<kFlagGenerateClInitCheck>(false); } - void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) { - DCHECK(HasAddress(load_kind)); - load_data_.address = address; + void SetLoadKind(LoadKind load_kind) { SetLoadKindInternal(load_kind); } @@ -5577,15 +5577,6 @@ class HLoadClass FINAL : public HInstruction { SetLoadKindInternal(load_kind); } - void SetLoadKindWithDexCacheReference(LoadKind load_kind, - const DexFile& dex_file, - uint32_t element_index) { - DCHECK(HasDexCacheReference(load_kind)); - DCHECK(IsSameDexFile(dex_file_, dex_file)); - load_data_.dex_cache_element_index = element_index; - SetLoadKindInternal(load_kind); - } - LoadKind GetLoadKind() const { return GetPackedField<LoadKindField>(); } @@ -5610,13 +5601,21 @@ class HLoadClass FINAL : public HInstruction { } bool CanCallRuntime() const { - return MustGenerateClinitCheck() || - (!IsReferrersClass() && !IsInDexCache()) || - NeedsAccessCheck(); + return NeedsAccessCheck() || + MustGenerateClinitCheck() || + GetLoadKind() == LoadKind::kDexCacheViaMethod || + GetLoadKind() == LoadKind::kBssEntry; } bool CanThrow() const OVERRIDE { - return CanCallRuntime(); + return NeedsAccessCheck() || + MustGenerateClinitCheck() || + // If the class is in the boot image, the lookup in the runtime call cannot throw. + // This keeps CanThrow() consistent between non-PIC (using kBootImageAddress) and + // PIC and subsequently avoids a DCE behavior dependency on the PIC option. + ((GetLoadKind() == LoadKind::kDexCacheViaMethod || + GetLoadKind() == LoadKind::kBssEntry) && + !IsInBootImage()); } ReferenceTypeInfo GetLoadedClassRTI() { @@ -5632,15 +5631,8 @@ class HLoadClass FINAL : public HInstruction { dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } - uint32_t GetDexCacheElementOffset() const; - - uint64_t GetAddress() const { - DCHECK(HasAddress(GetLoadKind())); - return load_data_.address; - } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { - return !IsReferrersClass(); + return GetLoadKind() == LoadKind::kDexCacheViaMethod; } static SideEffects SideEffectsForArchRuntimeCalls() { @@ -5649,17 +5641,9 @@ class HLoadClass FINAL : public HInstruction { bool IsReferrersClass() const { return GetLoadKind() == LoadKind::kReferrersClass; } bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } - bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); } bool IsInBootImage() const { return GetPackedFlag<kFlagIsInBootImage>(); } bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); } - void MarkInDexCache() { - SetPackedFlag<kFlagIsInDexCache>(true); - DCHECK(!NeedsEnvironment()); - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } - void MarkInBootImage() { SetPackedFlag<kFlagIsInBootImage>(true); } @@ -5676,12 +5660,15 @@ class HLoadClass FINAL : public HInstruction { return Primitive::kPrimNot; } + Handle<mirror::Class> GetClass() const { + return klass_; + } + DECLARE_INSTRUCTION(LoadClass); private: static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits; - static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1; - static constexpr size_t kFlagIsInBootImage = kFlagIsInDexCache + 1; + static constexpr size_t kFlagIsInBootImage = kFlagNeedsAccessCheck + 1; // Whether this instruction must generate the initialization check. // Used for code generation. static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInBootImage + 1; @@ -5693,35 +5680,24 @@ class HLoadClass FINAL : public HInstruction { using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>; static bool HasTypeReference(LoadKind load_kind) { - return load_kind == LoadKind::kBootImageLinkTimeAddress || + return load_kind == LoadKind::kReferrersClass || + load_kind == LoadKind::kBootImageLinkTimeAddress || load_kind == LoadKind::kBootImageLinkTimePcRelative || - load_kind == LoadKind::kDexCacheViaMethod || - load_kind == LoadKind::kReferrersClass; - } - - static bool HasAddress(LoadKind load_kind) { - return load_kind == LoadKind::kBootImageAddress || - load_kind == LoadKind::kJitTableAddress; - } - - static bool HasDexCacheReference(LoadKind load_kind) { - return load_kind == LoadKind::kDexCachePcRelative; + load_kind == LoadKind::kBssEntry || + load_kind == LoadKind::kDexCacheViaMethod; } void SetLoadKindInternal(LoadKind load_kind); // The special input is the HCurrentMethod for kDexCacheViaMethod or kReferrersClass. // For other load kinds it's empty or possibly some architecture-specific instruction - // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative. + // for PC-relative loads, i.e. kBssEntry or kBootImageLinkTimePcRelative. HUserRecord<HInstruction*> special_input_; const dex::TypeIndex type_index_; const DexFile& dex_file_; - union { - uint32_t dex_cache_element_index; // Only for dex cache reference. - uint64_t address; // Up to 64-bit, needed for kJitTableAddress on 64-bit targets. - } load_data_; + Handle<mirror::Class> klass_; ReferenceTypeInfo loaded_class_rti_; @@ -5730,19 +5706,13 @@ class HLoadClass FINAL : public HInstruction { std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); // Note: defined outside class to see operator<<(., HLoadClass::LoadKind). -inline uint32_t HLoadClass::GetDexCacheElementOffset() const { - DCHECK(HasDexCacheReference(GetLoadKind())) << GetLoadKind(); - return load_data_.dex_cache_element_index; -} - -// Note: defined outside class to see operator<<(., HLoadClass::LoadKind). inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, // including literal pool loads, which are PC-relative too. DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || - GetLoadKind() == LoadKind::kDexCachePcRelative || GetLoadKind() == LoadKind::kBootImageLinkTimeAddress || - GetLoadKind() == LoadKind::kBootImageAddress) << GetLoadKind(); + GetLoadKind() == LoadKind::kBootImageAddress || + GetLoadKind() == LoadKind::kBssEntry) << GetLoadKind(); DCHECK(special_input_.GetInstruction() == nullptr); special_input_ = HUserRecord<HInstruction*>(special_input); special_input->AddUseAt(this, 0); @@ -5770,15 +5740,15 @@ class HLoadString FINAL : public HInstruction { // Used for strings outside boot image when .bss is accessible with a PC-relative load. kBssEntry, + // Load from the root table associated with the JIT compiled method. + kJitTableAddress, + // Load from resolved strings array accessed through the class loaded from // the compiled method's own ArtMethod*. This is the default access type when // all other types are unavailable. kDexCacheViaMethod, - // Load from the root table associated with the JIT compiled method. - kJitTableAddress, - - kLast = kJitTableAddress, + kLast = kDexCacheViaMethod, }; HLoadString(HCurrentMethod* current_method, @@ -5870,7 +5840,7 @@ class HLoadString FINAL : public HInstruction { // The special input is the HCurrentMethod for kDexCacheViaMethod. // For other load kinds it's empty or possibly some architecture-specific instruction - // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative. + // for PC-relative loads, i.e. kBssEntry or kBootImageLinkTimePcRelative. HUserRecord<HInstruction*> special_input_; dex::StringIndex string_index_; @@ -5922,7 +5892,10 @@ class HClinitCheck FINAL : public HExpression<1> { bool CanThrow() const OVERRIDE { return true; } - HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); } + HLoadClass* GetLoadClass() const { + DCHECK(InputAt(0)->IsLoadClass()); + return InputAt(0)->AsLoadClass(); + } DECLARE_INSTRUCTION(ClinitCheck); @@ -6788,6 +6761,23 @@ inline void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks, std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end()); } +/* + * Hunt "under the hood" of array lengths (leading to array references), + * null checks (also leading to array references), and new arrays + * (leading to the actual length). This makes it more likely related + * instructions become actually comparable. + */ +inline HInstruction* HuntForDeclaration(HInstruction* instruction) { + while (instruction->IsArrayLength() || + instruction->IsNullCheck() || + instruction->IsNewArray()) { + instruction = instruction->IsNewArray() + ? instruction->AsNewArray()->GetLength() + : instruction->InputAt(0); + } + return instruction; +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_H_ diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc index 5d9a6528ca..7686ba851b 100644 --- a/compiler/optimizing/nodes_test.cc +++ b/compiler/optimizing/nodes_test.cc @@ -52,7 +52,7 @@ TEST(Node, RemoveInstruction) { exit_block->AddInstruction(new (&allocator) HExit()); HEnvironment* environment = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, null_check); + &allocator, 1, graph->GetArtMethod(), 0, null_check); null_check->SetRawEnvironment(environment); environment->SetRawEnvAt(0, parameter); parameter->AddEnvUseAt(null_check->GetEnvironment(), 0); @@ -137,7 +137,7 @@ TEST(Node, ParentEnvironment) { ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement()); HEnvironment* environment = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, with_environment); + &allocator, 1, graph->GetArtMethod(), 0, with_environment); ArenaVector<HInstruction*> array(allocator.Adapter()); array.push_back(parameter1); @@ -148,13 +148,13 @@ TEST(Node, ParentEnvironment) { ASSERT_TRUE(parameter1->GetEnvUses().HasExactlyOneElement()); HEnvironment* parent1 = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr); + &allocator, 1, graph->GetArtMethod(), 0, nullptr); parent1->CopyFrom(array); ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u); HEnvironment* parent2 = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr); + &allocator, 1, graph->GetArtMethod(), 0, nullptr); parent2->CopyFrom(array); parent1->SetAndCopyParentChain(&allocator, parent2); diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc index e321b9e3aa..a0fdde169d 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.cc +++ b/compiler/optimizing/pc_relative_fixups_mips.cc @@ -62,8 +62,9 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); switch (load_kind) { case HLoadClass::LoadKind::kBootImageLinkTimeAddress: - case HLoadClass::LoadKind::kBootImageAddress: case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBssEntry: // Add a base register for PC-relative literals on R2. InitializePCRelativeBasePointer(); load_class->AddSpecialInput(base_); diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index b1fdb1792d..2befc8ca4e 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -83,7 +83,7 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { void VisitLoadClass(HLoadClass* load_class) OVERRIDE { HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || - load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + load_kind == HLoadClass::LoadKind::kBssEntry) { InitializePCRelativeBasePointer(); load_class->AddSpecialInput(base_); } diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index db7c1fbb06..efbaf6c221 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -16,6 +16,9 @@ #include "prepare_for_register_allocation.h" +#include "jni_internal.h" +#include "well_known_classes.h" + namespace art { void PrepareForRegisterAllocation::Run() { @@ -42,16 +45,12 @@ void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { if (check->IsStringCharAt()) { // Add a fake environment for String.charAt() inline info as we want // the exception to appear as being thrown from there. - const DexFile& dex_file = check->GetEnvironment()->GetDexFile(); - DCHECK_STREQ(dex_file.PrettyMethod(check->GetStringCharAtMethodIndex()).c_str(), - "char java.lang.String.charAt(int)"); + ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); ArenaAllocator* arena = GetGraph()->GetArena(); HEnvironment* environment = new (arena) HEnvironment(arena, /* number_of_vregs */ 0u, - dex_file, - check->GetStringCharAtMethodIndex(), + char_at_method, /* dex_pc */ DexFile::kDexNoIndex, - kVirtual, check); check->InsertRawEnvironment(environment); } @@ -199,8 +198,7 @@ bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, return false; } if (user_environment->GetDexPc() != input_environment->GetDexPc() || - user_environment->GetMethodIdx() != input_environment->GetMethodIdx() || - !IsSameDexFile(user_environment->GetDexFile(), input_environment->GetDexFile())) { + user_environment->GetMethod() != input_environment->GetMethod()) { return false; } user_environment = user_environment->GetParent(); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index f8a4469712..b02f2509ab 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -295,13 +295,13 @@ static void BoundTypeForClassCheck(HInstruction* check) { } if (check->IsIf()) { - HBasicBlock* trueBlock = check->IsEqual() + HBasicBlock* trueBlock = compare->IsEqual() ? check->AsIf()->IfTrueSuccessor() : check->AsIf()->IfFalseSuccessor(); BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti); } else { DCHECK(check->IsDeoptimize()); - if (check->IsEqual()) { + if (compare->IsEqual()) { BoundTypeIn(receiver, check->GetBlock(), check, class_rti); } } @@ -499,18 +499,19 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) { // Calls to String.<init> are replaced with a StringFactory. if (kIsDebugBuild) { - HInvoke* invoke = instr->AsInvoke(); + HInvokeStaticOrDirect* invoke = instr->AsInvokeStaticOrDirect(); ClassLinker* cl = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); StackHandleScope<2> hs(self); + const DexFile& dex_file = *invoke->GetTargetMethod().dex_file; Handle<mirror::DexCache> dex_cache( - hs.NewHandle(FindDexCacheWithHint(self, invoke->GetDexFile(), hint_dex_cache_))); + hs.NewHandle(FindDexCacheWithHint(self, dex_file, hint_dex_cache_))); // Use a null loader. We should probably use the compiling method's class loader, // but then we would need to pass it to RTPVisitor just for this debug check. Since // the method is from the String class, the null loader is good enough. Handle<mirror::ClassLoader> loader; ArtMethod* method = cl->ResolveMethod<ClassLinker::kNoICCECheckForCache>( - invoke->GetDexFile(), invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); + dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr); @@ -547,11 +548,13 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* } void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) { - UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); + ScopedObjectAccess soa(Thread::Current()); + SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true); } void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { - UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); + ScopedObjectAccess soa(Thread::Current()); + SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true); } static mirror::Class* GetClassFromDexCache(Thread* self, @@ -619,14 +622,10 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - // Get type from dex cache assuming it was populated by the verifier. - mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), - instr->GetDexFile(), - instr->GetTypeIndex(), - hint_dex_cache_); - if (IsAdmissible(resolved_class)) { + Handle<mirror::Class> resolved_class = instr->GetClass(); + if (IsAdmissible(resolved_class.Get())) { instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( - handle_cache_->NewHandle(resolved_class), /* is_exact */ true)); + resolved_class, /* is_exact */ true)); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); @@ -843,12 +842,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) { } ScopedObjectAccess soa(Thread::Current()); - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = - FindDexCacheWithHint(soa.Self(), instr->GetDexFile(), hint_dex_cache_); - PointerSize pointer_size = cl->GetImagePointerSize(); - ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size); - mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size); + ArtMethod* method = instr->GetResolvedMethod(); + mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false); SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index dc8ee23ba4..c5294107ae 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -133,72 +133,65 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { void HSharpening::ProcessLoadClass(HLoadClass* load_class) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - const DexFile& dex_file = load_class->GetDexFile(); - dex::TypeIndex type_index = load_class->GetTypeIndex(); - Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() - : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); - mirror::Class* cls = dex_cache->GetResolvedType(type_index); - SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_); + SharpenClass(load_class, codegen_, compiler_driver_); } void HSharpening::SharpenClass(HLoadClass* load_class, - mirror::Class* klass, - VariableSizedHandleScope* handles, CodeGenerator* codegen, CompilerDriver* compiler_driver) { - ScopedAssertNoThreadSuspension sants("Sharpening class in compiler"); + Handle<mirror::Class> klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) << load_class->GetLoadKind(); - DCHECK(!load_class->IsInDexCache()) << "HLoadClass should not be optimized before sharpening."; DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening."; + if (load_class->NeedsAccessCheck()) { + // We need to call the runtime anyway, so we simply get the class as that call's return value. + return; + } + + if (load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) { + // Loading from the ArtMethod* is the most efficient retrieval in code size. + // TODO: This may not actually be true for all architectures and + // locations of target classes. The additional register pressure + // for using the ArtMethod* should be considered. + return; + } + const DexFile& dex_file = load_class->GetDexFile(); dex::TypeIndex type_index = load_class->GetTypeIndex(); - bool is_in_dex_cache = false; bool is_in_boot_image = false; HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1); - uint64_t address = 0u; // Class or dex cache element address. Runtime* runtime = Runtime::Current(); if (codegen->GetCompilerOptions().IsBootImage()) { // Compiling boot image. Check if the class is a boot image class. DCHECK(!runtime->UseJitCompilation()); if (!compiler_driver->GetSupportBootImageFixup()) { - // MIPS64 or compiler_driver_test. Do not sharpen. + // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; - } else if ((klass != nullptr) && compiler_driver->IsImageClass( + } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass( dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { is_in_boot_image = true; - is_in_dex_cache = true; desired_load_kind = codegen->GetCompilerOptions().GetCompilePic() ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative : HLoadClass::LoadKind::kBootImageLinkTimeAddress; } else { - // Not a boot image class. We must go through the dex cache. + // Not a boot image class. DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); - desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative; + desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } else { - is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass); + is_in_boot_image = (klass.Get() != nullptr) && + runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - is_in_dex_cache = (klass != nullptr); if (is_in_boot_image) { // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(klass); - } else if (is_in_dex_cache) { + } else if (klass.Get() != nullptr) { desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; - // We store in the address field the location of the stack reference maintained - // by the handle. We do this now so that the code generation does not need to figure - // out which class loader to use. - address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference()); } else { // Class not loaded yet. This happens when the dex code requesting // this `HLoadClass` hasn't been executed in the interpreter. @@ -209,15 +202,9 @@ void HSharpening::SharpenClass(HLoadClass* load_class, } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) { // AOT app compilation. Check if the class is in the boot image. desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(klass); } else { // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. - // Use PC-relative load from the dex cache if the dex file belongs - // to the oat file that we're currently compiling. - desired_load_kind = - ContainsElement(compiler_driver->GetDexFilesForOatFile(), &load_class->GetDexFile()) - ? HLoadClass::LoadKind::kDexCachePcRelative - : HLoadClass::LoadKind::kDexCacheViaMethod; + desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } DCHECK_NE(desired_load_kind, static_cast<HLoadClass::LoadKind>(-1)); @@ -226,42 +213,18 @@ void HSharpening::SharpenClass(HLoadClass* load_class, load_class->MarkInBootImage(); } - if (load_class->NeedsAccessCheck()) { - // We need to call the runtime anyway, so we simply get the class as that call's return value. - return; - } - - if (load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) { - // Loading from the ArtMethod* is the most efficient retrieval in code size. - // TODO: This may not actually be true for all architectures and - // locations of target classes. The additional register pressure - // for using the ArtMethod* should be considered. - return; - } - - if (is_in_dex_cache) { - load_class->MarkInDexCache(); - } - HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind); switch (load_kind) { case HLoadClass::LoadKind::kBootImageLinkTimeAddress: case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBssEntry: case HLoadClass::LoadKind::kDexCacheViaMethod: load_class->SetLoadKindWithTypeReference(load_kind, dex_file, type_index); break; case HLoadClass::LoadKind::kBootImageAddress: case HLoadClass::LoadKind::kJitTableAddress: - DCHECK_NE(address, 0u); - load_class->SetLoadKindWithAddress(load_kind, address); - break; - case HLoadClass::LoadKind::kDexCachePcRelative: { - PointerSize pointer_size = InstructionSetPointerSize(codegen->GetInstructionSet()); - DexCacheArraysLayout layout(pointer_size, &dex_file); - size_t element_index = layout.TypeOffset(type_index); - load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index); + load_class->SetLoadKind(load_kind); break; - } default: LOG(FATAL) << "Unexpected load kind: " << load_kind; UNREACHABLE(); @@ -274,7 +237,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { const DexFile& dex_file = load_string->GetDexFile(); dex::StringIndex string_index = load_string->GetStringIndex(); - HLoadString::LoadKind desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; + HLoadString::LoadKind desired_load_kind = static_cast<HLoadString::LoadKind>(-1); { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); @@ -297,8 +260,8 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { ? HLoadString::LoadKind::kBootImageLinkTimePcRelative : HLoadString::LoadKind::kBootImageLinkTimeAddress; } else { - // MIPS64 or compiler_driver_test. Do not sharpen. - DCHECK_EQ(desired_load_kind, HLoadString::LoadKind::kDexCacheViaMethod); + // compiler_driver_test. Do not sharpen. + desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; } } else if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. @@ -310,6 +273,8 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } else { desired_load_kind = HLoadString::LoadKind::kJitTableAddress; } + } else { + desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; } } else { // AOT app compilation. Try to lookup the string without allocating if not found. @@ -326,6 +291,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { load_string->SetString(handles_->NewHandle(string)); } } + DCHECK_NE(desired_load_kind, static_cast<HLoadString::LoadKind>(-1)); HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind); load_string->SetLoadKind(load_kind); diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index ae5ccb33ab..ae3d83ef2c 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -49,8 +49,6 @@ class HSharpening : public HOptimization { // Used internally but also by the inliner. static void SharpenClass(HLoadClass* load_class, - mirror::Class* klass, - VariableSizedHandleScope* handles, CodeGenerator* codegen, CompilerDriver* compiler_driver) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index fc8af6462a..6087e36507 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -13,8 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "stack_map_stream.h" +#include "art_method.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + namespace art { void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, @@ -98,15 +103,27 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t current_dex_register_++; } -void StackMapStream::BeginInlineInfoEntry(uint32_t method_index, +static bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) { + // Note: the runtime is null only for unit testing. + return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler(); +} + +void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, - InvokeType invoke_type, - uint32_t num_dex_registers) { + uint32_t num_dex_registers, + const DexFile* outer_dex_file) { DCHECK(!in_inline_frame_); in_inline_frame_ = true; - current_inline_info_.method_index = method_index; + if (EncodeArtMethodInInlineInfo(method)) { + current_inline_info_.method = method; + } else { + if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) { + ScopedObjectAccess soa(Thread::Current()); + DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile())); + } + current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); + } current_inline_info_.dex_pc = dex_pc; - current_inline_info_.invoke_type = invoke_type; current_inline_info_.num_dex_registers = num_dex_registers; current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size(); if (num_dex_registers != 0) { @@ -229,25 +246,32 @@ size_t StackMapStream::ComputeDexRegisterMapsSize() const { void StackMapStream::ComputeInlineInfoEncoding() { uint32_t method_index_max = 0; uint32_t dex_pc_max = DexFile::kDexNoIndex; - uint32_t invoke_type_max = 0; + uint32_t extra_data_max = 0; uint32_t inline_info_index = 0; for (const StackMapEntry& entry : stack_maps_) { for (size_t j = 0; j < entry.inlining_depth; ++j) { InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - method_index_max = std::max(method_index_max, inline_entry.method_index); + if (inline_entry.method == nullptr) { + method_index_max = std::max(method_index_max, inline_entry.method_index); + extra_data_max = std::max(extra_data_max, 1u); + } else { + method_index_max = std::max( + method_index_max, High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + extra_data_max = std::max( + extra_data_max, Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + } if (inline_entry.dex_pc != DexFile::kDexNoIndex && (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) { dex_pc_max = inline_entry.dex_pc; } - invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type)); } } DCHECK_EQ(inline_info_index, inline_infos_.size()); inline_info_encoding_.SetFromSizes(method_index_max, dex_pc_max, - invoke_type_max, + extra_data_max, dex_register_maps_size_); } @@ -354,9 +378,20 @@ void StackMapStream::FillIn(MemoryRegion region) { DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; - inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index); + if (inline_entry.method != nullptr) { + inline_info.SetMethodIndexAtDepth( + inline_info_encoding_, + depth, + High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + inline_info.SetExtraDataAtDepth( + inline_info_encoding_, + depth, + Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + } else { + inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index); + inline_info.SetExtraDataAtDepth(inline_info_encoding_, depth, 1); + } inline_info.SetDexPcAtDepth(inline_info_encoding_, depth, inline_entry.dex_pc); - inline_info.SetInvokeTypeAtDepth(inline_info_encoding_, depth, inline_entry.invoke_type); if (inline_entry.num_dex_registers == 0) { // No dex map available. inline_info.SetDexRegisterMapOffsetAtDepth(inline_info_encoding_, @@ -544,10 +579,13 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, d), inline_entry.dex_pc); - DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d), - inline_entry.method_index); - DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, d), - inline_entry.invoke_type); + if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, d)) { + DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info_encoding, d), + inline_entry.method); + } else { + DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d), + inline_entry.method_index); + } CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapAtDepth( diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 53a9795d52..d6f42b373c 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -109,8 +109,8 @@ class StackMapStream : public ValueObject { struct InlineInfoEntry { uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. + ArtMethod* method; uint32_t method_index; - InvokeType invoke_type; uint32_t num_dex_registers; BitVector* live_dex_registers_mask; size_t dex_register_locations_start_index; @@ -126,10 +126,10 @@ class StackMapStream : public ValueObject { void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value); - void BeginInlineInfoEntry(uint32_t method_index, + void BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, - InvokeType invoke_type, - uint32_t num_dex_registers); + uint32_t num_dex_registers, + const DexFile* outer_dex_file = nullptr); void EndInlineInfoEntry(); size_t GetNumberOfStackMaps() const { diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 967fd96561..22810ea4f7 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -16,6 +16,7 @@ #include "stack_map.h" +#include "art_method.h" #include "base/arena_bit_vector.h" #include "stack_map_stream.h" @@ -128,6 +129,7 @@ TEST(StackMapTest, Test2) { ArenaPool pool; ArenaAllocator arena(&pool); StackMapStream stream(&arena); + ArtMethod art_method; ArenaBitVector sp_mask1(&arena, 0, true); sp_mask1.SetBit(2); @@ -137,9 +139,9 @@ TEST(StackMapTest, Test2) { stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. - stream.BeginInlineInfoEntry(82, 3, kDirect, number_of_dex_registers_in_inline_info); + stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(42, 2, kStatic, number_of_dex_registers_in_inline_info); + stream.BeginInlineInfoEntry(&art_method, 2, number_of_dex_registers_in_inline_info); stream.EndInlineInfoEntry(); stream.EndStackMapEntry(); @@ -238,12 +240,10 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map_encoding)); InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info_encoding)); - ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); } // Second stack map. @@ -662,6 +662,7 @@ TEST(StackMapTest, InlineTest) { ArenaPool pool; ArenaAllocator arena(&pool); StackMapStream stream(&arena); + ArtMethod art_method; ArenaBitVector sp_mask1(&arena, 0, true); sp_mask1.SetBit(2); @@ -672,10 +673,10 @@ TEST(StackMapTest, InlineTest) { stream.AddDexRegisterEntry(Kind::kInStack, 0); stream.AddDexRegisterEntry(Kind::kConstant, 4); - stream.BeginInlineInfoEntry(42, 2, kStatic, 1); + stream.BeginInlineInfoEntry(&art_method, 2, 1); stream.AddDexRegisterEntry(Kind::kInStack, 8); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(82, 3, kStatic, 3); + stream.BeginInlineInfoEntry(&art_method, 3, 3); stream.AddDexRegisterEntry(Kind::kInStack, 16); stream.AddDexRegisterEntry(Kind::kConstant, 20); stream.AddDexRegisterEntry(Kind::kInRegister, 15); @@ -688,15 +689,15 @@ TEST(StackMapTest, InlineTest) { stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); - stream.BeginInlineInfoEntry(42, 2, kDirect, 1); + stream.BeginInlineInfoEntry(&art_method, 2, 1); stream.AddDexRegisterEntry(Kind::kInStack, 12); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(82, 3, kStatic, 3); + stream.BeginInlineInfoEntry(&art_method, 3, 3); stream.AddDexRegisterEntry(Kind::kInStack, 80); stream.AddDexRegisterEntry(Kind::kConstant, 10); stream.AddDexRegisterEntry(Kind::kInRegister, 5); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(52, 5, kVirtual, 0); + stream.BeginInlineInfoEntry(&art_method, 5, 0); stream.EndInlineInfoEntry(); stream.EndStackMapEntry(); @@ -712,12 +713,12 @@ TEST(StackMapTest, InlineTest) { stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); - stream.BeginInlineInfoEntry(42, 2, kVirtual, 0); + stream.BeginInlineInfoEntry(&art_method, 2, 0); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(52, 5, kInterface, 1); + stream.BeginInlineInfoEntry(&art_method, 5, 1); stream.AddDexRegisterEntry(Kind::kInRegister, 2); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(52, 10, kStatic, 2); + stream.BeginInlineInfoEntry(&art_method, 10, 2); stream.AddDexRegisterEntry(Kind::kNone, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 3); stream.EndInlineInfoEntry(); @@ -743,11 +744,9 @@ TEST(StackMapTest, InlineTest) { InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding); ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info_encoding)); ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1); ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); @@ -769,14 +768,11 @@ TEST(StackMapTest, InlineTest) { InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding); ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info_encoding)); ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1); ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); @@ -810,14 +806,11 @@ TEST(StackMapTest, InlineTest) { InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding); ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info_encoding)); ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2)); ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info_encoding, 0)); diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index a3fce02970..f132e2737d 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5610,7 +5610,7 @@ const char* const VixlJniHelpersResults[] = { " 214: ecbd 8a10 vpop {s16-s31}\n", " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", " 21c: 4660 mov r0, ip\n", - " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\n", + " 21e: f8d9 c2a4 ldr.w ip, [r9, #676] ; 0x2a4\n", " 222: 47e0 blx ip\n", nullptr }; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index cd30872986..d3b15ac8cf 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -350,6 +350,38 @@ void X86Assembler::movaps(XmmRegister dst, XmmRegister src) { } +void X86Assembler::movaps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst, src); +} + + +void X86Assembler::movups(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void X86Assembler::movaps(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src, dst); +} + + +void X86Assembler::movups(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + void X86Assembler::movss(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -467,6 +499,83 @@ void X86Assembler::divss(XmmRegister dst, const Address& src) { } +void X86Assembler::addps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::subps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::mulps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::divps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::movapd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::movapd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst, src); +} + + +void X86Assembler::movupd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void X86Assembler::movapd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src, dst); +} + + +void X86Assembler::movupd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + void X86Assembler::flds(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xD9); @@ -638,6 +747,42 @@ void X86Assembler::divsd(XmmRegister dst, const Address& src) { } +void X86Assembler::addpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::subpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::mulpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::divpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 114986b3e7..a93616c3e5 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -371,7 +371,12 @@ class X86Assembler FINAL : public Assembler { void setb(Condition condition, Register dst); - void movaps(XmmRegister dst, XmmRegister src); + void movaps(XmmRegister dst, XmmRegister src); // move + void movaps(XmmRegister dst, const Address& src); // load aligned + void movups(XmmRegister dst, const Address& src); // load unaligned + void movaps(const Address& dst, XmmRegister src); // store aligned + void movups(const Address& dst, XmmRegister src); // store unaligned + void movss(XmmRegister dst, const Address& src); void movss(const Address& dst, XmmRegister src); void movss(XmmRegister dst, XmmRegister src); @@ -388,6 +393,17 @@ class X86Assembler FINAL : public Assembler { void divss(XmmRegister dst, XmmRegister src); void divss(XmmRegister dst, const Address& src); + void addps(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subps(XmmRegister dst, XmmRegister src); + void mulps(XmmRegister dst, XmmRegister src); + void divps(XmmRegister dst, XmmRegister src); + + void movapd(XmmRegister dst, XmmRegister src); // move + void movapd(XmmRegister dst, const Address& src); // load aligned + void movupd(XmmRegister dst, const Address& src); // load unaligned + void movapd(const Address& dst, XmmRegister src); // store aligned + void movupd(const Address& dst, XmmRegister src); // store unaligned + void movsd(XmmRegister dst, const Address& src); void movsd(const Address& dst, XmmRegister src); void movsd(XmmRegister dst, XmmRegister src); @@ -409,6 +425,11 @@ class X86Assembler FINAL : public Assembler { void divsd(XmmRegister dst, XmmRegister src); void divsd(XmmRegister dst, const Address& src); + void addpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subpd(XmmRegister dst, XmmRegister src); + void mulpd(XmmRegister dst, XmmRegister src); + void divpd(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 9bae6c20bd..4d60a12cb9 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -423,6 +423,98 @@ TEST_F(AssemblerX86Test, TestlAddressImmediate) { DriverStr(expected, "TestlAddressImmediate"); } +TEST_F(AssemblerX86Test, Movaps) { + DriverStr(RepeatFF(&x86::X86Assembler::movaps, "movaps %{reg2}, %{reg1}"), "movaps"); +} + +TEST_F(AssemblerX86Test, MovapsAddr) { + GetAssembler()->movaps(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movaps(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movaps 0x4(%ESP), %xmm0\n" + "movaps %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movaps_address"); +} + +TEST_F(AssemblerX86Test, MovupsAddr) { + GetAssembler()->movups(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movups(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movups 0x4(%ESP), %xmm0\n" + "movups %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movups_address"); +} + +TEST_F(AssemblerX86Test, Movapd) { + DriverStr(RepeatFF(&x86::X86Assembler::movapd, "movapd %{reg2}, %{reg1}"), "movapd"); +} + +TEST_F(AssemblerX86Test, MovapdAddr) { + GetAssembler()->movapd(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movapd(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movapd 0x4(%ESP), %xmm0\n" + "movapd %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movapd_address"); +} + +TEST_F(AssemblerX86Test, MovupdAddr) { + GetAssembler()->movupd(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movupd(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movupd 0x4(%ESP), %xmm0\n" + "movupd %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movupd_address"); +} + +TEST_F(AssemblerX86Test, AddPS) { + GetAssembler()->addps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "addps %xmm1, %xmm0\n"; + DriverStr(expected, "addps"); +} + +TEST_F(AssemblerX86Test, AddPD) { + GetAssembler()->addpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "addpd %xmm1, %xmm0\n"; + DriverStr(expected, "addpd"); +} + +TEST_F(AssemblerX86Test, SubPS) { + GetAssembler()->subps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "subps %xmm1, %xmm0\n"; + DriverStr(expected, "subps"); +} + +TEST_F(AssemblerX86Test, SubPD) { + GetAssembler()->subpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "subpd %xmm1, %xmm0\n"; + DriverStr(expected, "subpd"); +} + +TEST_F(AssemblerX86Test, MulPS) { + GetAssembler()->mulps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "mulps %xmm1, %xmm0\n"; + DriverStr(expected, "mulps"); +} + +TEST_F(AssemblerX86Test, MulPD) { + GetAssembler()->mulpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "mulpd %xmm1, %xmm0\n"; + DriverStr(expected, "mulpd"); +} + +TEST_F(AssemblerX86Test, DivPS) { + GetAssembler()->divps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "divps %xmm1, %xmm0\n"; + DriverStr(expected, "divps"); +} + +TEST_F(AssemblerX86Test, DivPD) { + GetAssembler()->divpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "divpd %xmm1, %xmm0\n"; + DriverStr(expected, "divpd"); +} + ///////////////// // Near labels // ///////////////// diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index e9a0607290..2366b68f11 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -386,6 +386,42 @@ void X86_64Assembler::movaps(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::movaps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movups(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movaps(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::movups(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src.LowBits(), dst); +} + + void X86_64Assembler::movss(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -539,6 +575,42 @@ void X86_64Assembler::divss(XmmRegister dst, const Address& src) { } +void X86_64Assembler::addps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::subps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::mulps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::divps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::flds(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xD9); @@ -560,6 +632,56 @@ void X86_64Assembler::fstps(const Address& dst) { } +void X86_64Assembler::movapd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movapd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movupd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movapd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::movupd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src.LowBits(), dst); +} + + void X86_64Assembler::movsd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF2); @@ -670,6 +792,46 @@ void X86_64Assembler::divsd(XmmRegister dst, const Address& src) { } +void X86_64Assembler::addpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::subpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::mulpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::divpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index acad86d161..5923a41fe3 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -390,7 +390,11 @@ class X86_64Assembler FINAL : public Assembler { void leaq(CpuRegister dst, const Address& src); void leal(CpuRegister dst, const Address& src); - void movaps(XmmRegister dst, XmmRegister src); + void movaps(XmmRegister dst, XmmRegister src); // move + void movaps(XmmRegister dst, const Address& src); // load aligned + void movups(XmmRegister dst, const Address& src); // load unaligned + void movaps(const Address& dst, XmmRegister src); // store aligned + void movups(const Address& dst, XmmRegister src); // store unaligned void movss(XmmRegister dst, const Address& src); void movss(const Address& dst, XmmRegister src); @@ -413,6 +417,17 @@ class X86_64Assembler FINAL : public Assembler { void divss(XmmRegister dst, XmmRegister src); void divss(XmmRegister dst, const Address& src); + void addps(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subps(XmmRegister dst, XmmRegister src); + void mulps(XmmRegister dst, XmmRegister src); + void divps(XmmRegister dst, XmmRegister src); + + void movapd(XmmRegister dst, XmmRegister src); // move + void movapd(XmmRegister dst, const Address& src); // load aligned + void movupd(XmmRegister dst, const Address& src); // load unaligned + void movapd(const Address& dst, XmmRegister src); // store aligned + void movupd(const Address& dst, XmmRegister src); // store unaligned + void movsd(XmmRegister dst, const Address& src); void movsd(const Address& dst, XmmRegister src); void movsd(XmmRegister dst, XmmRegister src); @@ -426,6 +441,11 @@ class X86_64Assembler FINAL : public Assembler { void divsd(XmmRegister dst, XmmRegister src); void divsd(XmmRegister dst, const Address& src); + void addpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subpd(XmmRegister dst, XmmRegister src); + void mulpd(XmmRegister dst, XmmRegister src); + void divpd(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index ff01429058..2812c34406 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -986,10 +986,50 @@ TEST_F(AssemblerX86_64Test, Movaps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movaps, "movaps %{reg2}, %{reg1}"), "movaps"); } +TEST_F(AssemblerX86_64Test, MovapsAddr) { + GetAssembler()->movaps(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movaps(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movaps 0x4(%RSP), %xmm0\n" + "movaps %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movaps_address"); +} + +TEST_F(AssemblerX86_64Test, MovupsAddr) { + GetAssembler()->movups(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movups(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movups 0x4(%RSP), %xmm0\n" + "movups %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movups_address"); +} + TEST_F(AssemblerX86_64Test, Movss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movss, "movss %{reg2}, %{reg1}"), "movss"); } +TEST_F(AssemblerX86_64Test, Movapd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::movapd, "movapd %{reg2}, %{reg1}"), "movapd"); +} + +TEST_F(AssemblerX86_64Test, MovapdAddr) { + GetAssembler()->movapd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movapd(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movapd 0x4(%RSP), %xmm0\n" + "movapd %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movapd_address"); +} + +TEST_F(AssemblerX86_64Test, MovupdAddr) { + GetAssembler()->movupd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movupd(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movupd 0x4(%RSP), %xmm0\n" + "movupd %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movupd_address"); +} + TEST_F(AssemblerX86_64Test, Movsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd"); } @@ -1010,6 +1050,14 @@ TEST_F(AssemblerX86_64Test, Addsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::addsd, "addsd %{reg2}, %{reg1}"), "addsd"); } +TEST_F(AssemblerX86_64Test, Addps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::addps, "addps %{reg2}, %{reg1}"), "addps"); +} + +TEST_F(AssemblerX86_64Test, Addpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::addpd, "addpd %{reg2}, %{reg1}"), "addpd"); +} + TEST_F(AssemblerX86_64Test, Subss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::subss, "subss %{reg2}, %{reg1}"), "subss"); } @@ -1018,6 +1066,14 @@ TEST_F(AssemblerX86_64Test, Subsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::subsd, "subsd %{reg2}, %{reg1}"), "subsd"); } +TEST_F(AssemblerX86_64Test, Subps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::subps, "subps %{reg2}, %{reg1}"), "subps"); +} + +TEST_F(AssemblerX86_64Test, Subpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::subpd, "subpd %{reg2}, %{reg1}"), "subpd"); +} + TEST_F(AssemblerX86_64Test, Mulss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulss, "mulss %{reg2}, %{reg1}"), "mulss"); } @@ -1026,6 +1082,14 @@ TEST_F(AssemblerX86_64Test, Mulsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulsd, "mulsd %{reg2}, %{reg1}"), "mulsd"); } +TEST_F(AssemblerX86_64Test, Mulps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulps, "mulps %{reg2}, %{reg1}"), "mulps"); +} + +TEST_F(AssemblerX86_64Test, Mulpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulpd, "mulpd %{reg2}, %{reg1}"), "mulpd"); +} + TEST_F(AssemblerX86_64Test, Divss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divss, "divss %{reg2}, %{reg1}"), "divss"); } @@ -1034,6 +1098,14 @@ TEST_F(AssemblerX86_64Test, Divsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divsd, "divsd %{reg2}, %{reg1}"), "divsd"); } +TEST_F(AssemblerX86_64Test, Divps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::divps, "divps %{reg2}, %{reg1}"), "divps"); +} + +TEST_F(AssemblerX86_64Test, Divpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 4f06a91448..5fc9972d09 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -1414,7 +1414,14 @@ TEST_F(VerifierDepsTest, VerifyDeps) { ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } - { + // The two tests below make sure that fiddling with the method kind + // (static, virtual, interface) is detected by `ValidateDependencies`. + + // An interface method lookup can succeed with a virtual method lookup on the same class. + // That's OK, as we only want to make sure there is a method being defined with the right + // flags. Therefore, polluting the interface methods with virtual methods does not have + // to fail verification. + if (resolution_kind != kVirtualMethodResolution) { VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); bool found = false; @@ -1433,7 +1440,8 @@ TEST_F(VerifierDepsTest, VerifyDeps) { ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } - { + // See comment above that applies the same way. + if (resolution_kind != kInterfaceMethodResolution) { VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); bool found = false; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 21b03eb8ba..e8a92c1914 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -64,7 +64,7 @@ #include "gc/space/space-inl.h" #include "image_writer.h" #include "interpreter/unstarted_runtime.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "leb128.h" #include "linker/buffered_output_stream.h" #include "linker/file_output_stream.h" @@ -1095,6 +1095,8 @@ class Dex2Oat FINAL { compiler_options_->GetNativeDebuggable() ? OatHeader::kTrueValue : OatHeader::kFalseValue); key_value_store_->Put(OatHeader::kCompilerFilter, CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter())); + key_value_store_->Put(OatHeader::kConcurrentCopying, + kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue); } // Parse the arguments from the command line. In case of an unrecognized option or impossible diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index cdb3b9fe2a..e86e560b1a 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -30,7 +30,7 @@ #include "base/macros.h" #include "dex_file-inl.h" #include "dex2oat_environment_test.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" #include "utils.h" diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 02274b25a3..75d47e4013 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -31,7 +31,7 @@ #include "dex_ir.h" #include "dexlayout.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" namespace art { diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index cac60900bc..1add6bfede 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -37,7 +37,7 @@ #include "dex_instruction-inl.h" #include "dex_visualize.h" #include "dex_writer.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "mem_map.h" #include "os.h" #include "utils.h" diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 5f8a118bde..ad599aed93 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -30,7 +30,7 @@ #include <fcntl.h> #include "base/logging.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "runtime.h" #include "mem_map.h" diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 7ae13a574b..9a73830f99 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -790,8 +790,6 @@ void PatchOat::FixupMethod(ArtMethod* object, ArtMethod* copy) { copy->SetDeclaringClass(RelocatedAddressOfPointer(object->GetDeclaringClass())); copy->SetDexCacheResolvedMethods( RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods(pointer_size)), pointer_size); - copy->SetDexCacheResolvedTypes( - RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes(pointer_size)), pointer_size); copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer( object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size); // No special handling for IMT conflict table since all pointers are moved by the same offset. diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h index d3c75b817a..be703abda8 100644 --- a/profman/profile_assistant.h +++ b/profman/profile_assistant.h @@ -21,7 +21,7 @@ #include <vector> #include "base/scoped_flock.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" namespace art { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 776c31a662..2f40fef42e 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -19,7 +19,7 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "profile_assistant.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "utils.h" namespace art { diff --git a/profman/profman.cc b/profman/profman.cc index e5384078f1..ffebb6a2ea 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -34,7 +34,7 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "runtime.h" #include "utils.h" #include "zip_archive.h" diff --git a/runtime/Android.bp b/runtime/Android.bp index 86019bf71c..81f174e43b 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -112,7 +112,7 @@ cc_defaults { "jit/debugger_interface.cc", "jit/jit.cc", "jit/jit_code_cache.cc", - "jit/offline_profiling_info.cc", + "jit/profile_compilation_info.cc", "jit/profiling_info.cc", "jit/profile_saver.cc", "jni_internal.cc", @@ -184,6 +184,7 @@ cc_defaults { "reference_table.cc", "reflection.cc", "runtime.cc", + "runtime_callbacks.cc", "runtime_options.cc", "signal_catcher.cc", "stack.cc", @@ -563,6 +564,7 @@ art_cc_test { "parsed_options_test.cc", "prebuilt_tools_test.cc", "reference_table_test.cc", + "runtime_callbacks_test.cc", "thread_pool_test.cc", "transaction_test.cc", "type_lookup_table_test.cc", diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 61d1607112..db1cad670d 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -856,27 +856,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * r0 = array, r1 = index, r2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - tst r0, r0 - bne art_quick_aput_obj_with_bound_check - b art_quick_throw_null_pointer_exception -END art_quick_aput_obj_with_null_and_bound_check - - .hidden art_quick_aput_obj_with_bound_check -ENTRY art_quick_aput_obj_with_bound_check - ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] - cmp r3, r1 - bhi art_quick_aput_obj - mov r0, r1 - mov r1, r3 - b art_quick_throw_array_bounds -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -2010,3 +1989,83 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg08, r8 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 + +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME r2 + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + mov r0, #0 @ initialize 64-bit JValue as zero. + str r0, [sp, #-4]! + .cfi_adjust_cfa_offset 4 + str r0, [sp, #-4]! + .cfi_adjust_cfa_offset 4 + mov r0, sp @ pass JValue for return result as first argument. + bl artInvokePolymorphic @ artInvokePolymorphic(JValue, receiver, Thread*, SP) + sub r0, 'A' @ return value is descriptor of handle's return type. + cmp r0, 'Z' - 'A' @ check if value is in bounds of handler table + bgt .Lcleanup_and_return @ and clean-up if not. + adr r1, .Lhandler_table + tbb [r0, r1] @ branch to handler for return value based on return type. + +.Lstart_of_handlers: +.Lstore_boolean_result: + ldrb r0, [sp] @ Copy boolean value to return value of this function. + b .Lcleanup_and_return +.Lstore_char_result: + ldrh r0, [sp] @ Copy char value to return value of this function. + b .Lcleanup_and_return +.Lstore_float_result: + vldr s0, [sp] @ Copy float value from JValue result to the context restored by + vstr s0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. + b .Lcleanup_and_return +.Lstore_double_result: + vldr d0, [sp] @ Copy double value from JValue result to the context restored by + vstr d0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. + b .Lcleanup_and_return +.Lstore_long_result: + ldr r1, [sp, #4] @ Copy the upper bits from JValue result to the context restored by + str r1, [sp, #80] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. + // Fall-through for lower bits. +.Lstore_int_result: + ldr r0, [sp] @ Copy int value to return value of this function. + // Fall-through to clean up and return. +.Lcleanup_and_return: + add sp, #8 + .cfi_adjust_cfa_offset -8 + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 + +.macro HANDLER_TABLE_OFFSET handler_label + .byte (\handler_label - .Lstart_of_handlers) / 2 +.endm + +.Lhandler_table: + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A + HANDLER_TABLE_OFFSET(.Lstore_int_result) // B (byte) + HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) + HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E + HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H + HANDLER_TABLE_OFFSET(.Lstore_int_result) // I (int) + HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K + HANDLER_TABLE_OFFSET(.Lstore_int_result) // L (object) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R + HANDLER_TABLE_OFFSET(.Lstore_int_result) // S (short) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y + HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) +.purgem HANDLER_TABLE_OFFSET +END art_quick_invoke_polymorphic diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 8b1e0388c6..00518e16be 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1404,33 +1404,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * x0 = array, x1 = index, x2 = value - * - * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We - * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by - * using index-zero-extension in load/stores. - * - * Temporaries: x3, x4 - * TODO: x4 OK? ip seems wrong here. - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - tst x0, x0 - bne art_quick_aput_obj_with_bound_check - b art_quick_throw_null_pointer_exception -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] - cmp w3, w1 - bhi art_quick_aput_obj - mov x0, x1 - mov x1, x3 - b art_quick_throw_array_bounds -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -1672,11 +1645,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -1764,13 +1733,6 @@ END art_quick_alloc_object_resolved_rosalloc // The common fast path code for art_quick_alloc_array_region_tlab. -.macro ALLOC_ARRAY_TLAB_FAST_PATH slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2 - // Check null class - cbz \wClass, \slowPathLabel - ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED \slowPathLabel, \xClass, \wClass, \xCount, \wCount, \xTemp0, \wTemp0, \xTemp1, \wTemp1, \xTemp2, \wTemp2 -.endm - -// The common fast path code for art_quick_alloc_array_region_tlab. .macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2 // Array classes are never finalizable or uninitialized, no need to check. ldr \wTemp0, [\xClass, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Load component type @@ -1907,64 +1869,31 @@ GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB art_quick_alloc_object_initialized_re // TODO: We could use this macro for the normal tlab allocator too. // The common code for art_quick_alloc_array_*region_tlab -.macro GENERATE_ALLOC_ARRAY_REGION_TLAB name, entrypoint, fast_path, is_resolved +.macro GENERATE_ALLOC_ARRAY_REGION_TLAB name, entrypoint, fast_path ENTRY \name // Fast path array allocation for region tlab allocation. - // x0: uint32_t type_idx + // x0: mirror::Class* type // x1: int32_t component_count - // x2: ArtMethod* method - // x3-x7: free. + // x2-x7: free. #if !defined(USE_READ_BARRIER) mvn x0, xzr // Read barrier must be enabled here. ret // Return -1. #endif -.if \is_resolved mov x3, x0 - // If already resolved, class is stored in x0 -.else - ldr x3, [x2, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64] // Load dex cache resolved types array - // Load the class (x2) - ldr w3, [x3, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] -.endif - // Most common case: GC is not marking. - ldr w4, [xSELF, #THREAD_IS_GC_MARKING_OFFSET] - cbnz x4, .Lmarking\name -.Ldo_allocation\name: \fast_path .Lslow_path\name, x3, w3, x1, w1, x4, w4, x5, w5, x6, w6 -.Lmarking\name: - // GC is marking, check the lock word of the class for the mark bit. - // If the class is null, go slow path. The check is required to read the lock word. - cbz w3, .Lslow_path\name - // Class is not null, check mark bit in lock word. - ldr w4, [x3, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - // If the bit is not zero, do the allocation. - tbnz w4, #LOCK_WORD_MARK_BIT_SHIFT, .Ldo_allocation\name - // The read barrier slow path. Mark - // the class. - stp x0, x1, [sp, #-32]! // Save registers (x0, x1, x2, lr). - stp x2, xLR, [sp, #16] - mov x0, x3 // Pass the class as the first param. - bl artReadBarrierMark - mov x3, x0 // Get the (marked) class back. - ldp x2, xLR, [sp, #16] - ldp x0, x1, [sp], #32 // Restore registers. - b .Ldo_allocation\name .Lslow_path\name: - // x0: uint32_t type_idx / mirror::Class* klass (if resolved) + // x0: mirror::Class* klass // x1: int32_t component_count - // x2: ArtMethod* method - // x3: Thread* self + // x2: Thread* self SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC - mov x3, xSELF // pass Thread::Current + mov x2, xSELF // pass Thread::Current bl \entrypoint RESTORE_SAVE_REFS_ONLY_FRAME RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END \name .endm -GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_region_tlab, artAllocArrayFromCodeRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH, 0 -// TODO: art_quick_alloc_array_resolved_region_tlab seems to not get called. Investigate compiler. -GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, 1 +GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED /* * Called by managed code when the thread has been asked to suspend. @@ -2567,3 +2496,82 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg26, w26, x26 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg27, w27, x27 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg28, w28, x28 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29 + +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC. + mov x2, xSELF + mov x3, sp + INCREASE_FRAME 16 // Reserve space for JValue result. + str xzr, [sp, #0] // Initialize result to zero. + mov x0, sp // Set r0 to point to result. + bl artInvokePolymorphic // ArtInvokePolymorphic(result, receiver, thread, save_area) + uxtb w0, w0 // Result is the return type descriptor as a char. + sub w0, w0, 'A' // Convert to zero based index. + cmp w0, 'Z' - 'A' + bhi .Lcleanup_and_return // Clean-up if out-of-bounds. + adrp x1, .Lhandler_table // Compute address of handler table. + add x1, x1, :lo12:.Lhandler_table + ldrb w0, [x1, w0, uxtw] // Lookup handler offset in handler table. + adr x1, .Lstart_of_handlers + add x0, x1, w0, sxtb #2 // Convert relative offset to absolute address. + br x0 // Branch to handler. + +.Lstart_of_handlers: +.Lstore_boolean_result: + ldrb w0, [sp] + b .Lcleanup_and_return +.Lstore_char_result: + ldrh w0, [sp] + b .Lcleanup_and_return +.Lstore_float_result: + ldr s0, [sp] + str s0, [sp, #32] + b .Lcleanup_and_return +.Lstore_double_result: + ldr d0, [sp] + str d0, [sp, #32] + b .Lcleanup_and_return +.Lstore_long_result: + ldr x0, [sp] + // Fall-through +.Lcleanup_and_return: + DECREASE_FRAME 16 + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 + + .section .rodata // Place handler table in read-only section away from text. + .align 2 +.macro HANDLER_TABLE_OFFSET handler_label + .byte (\handler_label - .Lstart_of_handlers) / 4 +.endm +.Lhandler_table: + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A + HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte) + HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) + HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E + HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H + HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int) + HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K + HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R + HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y + HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) + .text + +END art_quick_invoke_polymorphic diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 5c569232ac..36f9ea78e1 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -142,16 +142,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickGetObjStatic), "Non-direct C stub marked direct."); // Array - qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; - static_assert(!IsDirectEntrypoint(kQuickAputObjectWithNullAndBoundCheck), - "Non-direct C stub marked direct."); - qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; - static_assert(!IsDirectEntrypoint(kQuickAputObjectWithBoundCheck), - "Non-direct C stub marked direct."); qpoints->pAputObject = art_quick_aput_obj; static_assert(!IsDirectEntrypoint(kQuickAputObject), "Non-direct C stub marked direct."); - qpoints->pHandleFillArrayData = art_quick_handle_fill_data; - static_assert(!IsDirectEntrypoint(kQuickHandleFillArrayData), "Non-direct C stub marked direct."); // JNI qpoints->pJniMethodStart = JniMethodStart; @@ -262,6 +254,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { art_quick_invoke_virtual_trampoline_with_access_check; static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck), "Non-direct C stub marked direct."); + qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 964ea563b0..76218fb542 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1389,28 +1389,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * a0 = array, a1 = index, a2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set - nop - b art_quick_throw_null_pointer_exception - nop -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) - sltu $t1, $a1, $t0 - bnez $t1, .Lart_quick_aput_obj_gp_set - nop - move $a0, $a1 - b art_quick_throw_array_bounds - move $a1, $t0 -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -2230,7 +2208,7 @@ ENTRY_NO_GP art_quick_indexof li $v0, -1 # return -1; sll $v0, $a2, 1 # $a0 += $a2 * 2 - addu $a0, $a0, $v0 # " " " " " + addu $a0, $a0, $v0 # " ditto " move $v0, $a2 # Set i to fromIndex. 1: @@ -2280,3 +2258,59 @@ ENTRY_NO_GP art_quick_string_compareto j $ra nop END art_quick_string_compareto + +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a2, rSELF # Make $a2 an alias for the current Thread. + addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context. + sw $zero, 20($sp) # Initialize JValue result. + sw $zero, 16($sp) + la $t9, artInvokePolymorphic + jalr $t9 # (result, receiver, Thread*, context) + addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result +.macro MATCH_RETURN_TYPE c, handler + li $t0, \c + beq $v0, $t0, \handler +.endm + MATCH_RETURN_TYPE 'V', .Lcleanup_and_return + MATCH_RETURN_TYPE 'L', .Lstore_int_result + MATCH_RETURN_TYPE 'I', .Lstore_int_result + MATCH_RETURN_TYPE 'J', .Lstore_long_result + MATCH_RETURN_TYPE 'B', .Lstore_int_result + MATCH_RETURN_TYPE 'C', .Lstore_char_result + MATCH_RETURN_TYPE 'D', .Lstore_double_result + MATCH_RETURN_TYPE 'F', .Lstore_float_result + MATCH_RETURN_TYPE 'S', .Lstore_int_result + MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result +.purgem MATCH_RETURN_TYPE + nop + b .Lcleanup_and_return + nop +.Lstore_boolean_result: + b .Lcleanup_and_return + lbu $v0, 16($sp) # Move byte from JValue result to return value register. +.Lstore_char_result: + b .Lcleanup_and_return + lhu $v0, 16($sp) # Move char from JValue result to return value register. +.Lstore_double_result: +.Lstore_float_result: + LDu $f0, $f1, 16, $sp, $t0 # Move double/float from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_long_result: + lw $v1, 20($sp) # Move upper bits from JValue result to return value register. + // Fall-through for lower bits. +.Lstore_int_result: + lw $v0, 16($sp) # Move lower bits from JValue result to return value register. + // Fall-through to clean up and return. +.Lcleanup_and_return: + lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ + RESTORE_SAVE_REFS_AND_ARGS_FRAME + bnez $t7, 1f # Success if no exception is pending. + nop + jalr $zero, $ra + nop +1: + DELIVER_PENDING_EXCEPTION +END art_quick_invoke_polymorphic diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc index 5606c1d858..5757906618 100644 --- a/runtime/arch/mips64/instruction_set_features_mips64.cc +++ b/runtime/arch/mips64/instruction_set_features_mips64.cc @@ -70,7 +70,7 @@ uint32_t Mips64InstructionSetFeatures::AsBitmap() const { } std::string Mips64InstructionSetFeatures::GetFeatureString() const { - return ""; + return "default"; } std::unique_ptr<const InstructionSetFeatures> diff --git a/runtime/arch/mips64/instruction_set_features_mips64_test.cc b/runtime/arch/mips64/instruction_set_features_mips64_test.cc index 1d037947fa..380c4e5433 100644 --- a/runtime/arch/mips64/instruction_set_features_mips64_test.cc +++ b/runtime/arch/mips64/instruction_set_features_mips64_test.cc @@ -27,7 +27,7 @@ TEST(Mips64InstructionSetFeaturesTest, Mips64Features) { ASSERT_TRUE(mips64_features.get() != nullptr) << error_msg; EXPECT_EQ(mips64_features->GetInstructionSet(), kMips64); EXPECT_TRUE(mips64_features->Equals(mips64_features.get())); - EXPECT_STREQ("", mips64_features->GetFeatureString().c_str()); + EXPECT_STREQ("default", mips64_features->GetFeatureString().c_str()); EXPECT_EQ(mips64_features->AsBitmap(), 0U); } diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 2a18d53aea..b53fd100fa 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1360,29 +1360,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * a0 = array, a1 = index, a2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - bne $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set - nop - b art_quick_throw_null_pointer_exception - .cpreturn # Restore gp from t8 in branch delay slot. -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - lwu $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) - sltu $t1, $a1, $t0 - bne $t1, $zero, .Lart_quick_aput_obj_gp_set - nop - move $a0, $a1 - move $a1, $t0 - b art_quick_throw_array_bounds - .cpreturn # Restore gp from t8 in branch delay slot. -END art_quick_aput_obj_with_bound_check - ENTRY art_quick_aput_obj beq $a2, $zero, .Ldo_aput_null nop @@ -2105,7 +2082,7 @@ ENTRY_NO_GP art_quick_indexof li $v0,-1 # return -1; sll $v0,$a2,1 # $a0 += $a2 * 2 - daddu $a0,$a0,$v0 # " " " " " + daddu $a0,$a0,$v0 # " ditto " move $v0,$a2 # Set i to fromIndex. 1: @@ -2124,4 +2101,61 @@ ENTRY_NO_GP art_quick_indexof nop END art_quick_indexof +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a2, rSELF # Make $a2 an alias for the current Thread. + move $a3, $sp # Make $a3 a pointer to the saved frame context. + daddiu $sp, $sp, -8 # Reserve space for JValue result. + .cfi_adjust_cfa_offset 8 + sd $zero, 0($sp) # Initialize JValue result. + jal artInvokePolymorphic # (result, receiver, Thread*, context) + move $a0, $sp # Make $a0 a pointer to the JValue result +.macro MATCH_RETURN_TYPE c, handler + li $t0, \c + beq $v0, $t0, \handler +.endm + MATCH_RETURN_TYPE 'V', .Lcleanup_and_return + MATCH_RETURN_TYPE 'L', .Lstore_ref_result + MATCH_RETURN_TYPE 'I', .Lstore_long_result + MATCH_RETURN_TYPE 'J', .Lstore_long_result + MATCH_RETURN_TYPE 'B', .Lstore_long_result + MATCH_RETURN_TYPE 'C', .Lstore_char_result + MATCH_RETURN_TYPE 'D', .Lstore_double_result + MATCH_RETURN_TYPE 'F', .Lstore_float_result + MATCH_RETURN_TYPE 'S', .Lstore_long_result + MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result +.purgem MATCH_RETURN_TYPE + nop + b .Lcleanup_and_return + nop +.Lstore_boolean_result: + b .Lcleanup_and_return + lbu $v0, 0($sp) # Move byte from JValue result to return value register. +.Lstore_char_result: + b .Lcleanup_and_return + lhu $v0, 0($sp) # Move char from JValue result to return value register. +.Lstore_double_result: +.Lstore_float_result: + b .Lcleanup_and_return + l.d $f0, 0($sp) # Move double/float from JValue result to return value register. +.Lstore_ref_result: + b .Lcleanup_and_return + lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. +.Lstore_long_result: + ld $v0, 0($sp) # Move long from JValue result to return value register. + // Fall-through to clean up and return. +.Lcleanup_and_return: + daddiu $sp, $sp, 8 # Remove space for JValue result. + .cfi_adjust_cfa_offset -8 + ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ + RESTORE_SAVE_REFS_AND_ARGS_FRAME + bnez $t0, 1f # Success if no exception is pending. + nop + jalr $zero, $ra + nop +1: + DELIVER_PENDING_EXCEPTION +END art_quick_invoke_polymorphic + .set pop diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index abd9046174..e79dc6029b 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -22,17 +22,8 @@ ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFrom // Called by managed code to allocate an object when the caller doesn't know whether it has access // to the created type. ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array. -THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate an array of a resolve class. -THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array when the caller doesn't know whether it has access -// to the created type. -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from bytes FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from chars @@ -65,16 +56,8 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_alloc_array ## c_suffix, artAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER + TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \ @@ -92,11 +75,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -107,11 +86,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) @@ -126,11 +101,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc) @@ -138,11 +109,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented) @@ -151,11 +118,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMa // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc) @@ -163,11 +126,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented) @@ -175,11 +134,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosA GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer) @@ -187,11 +142,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented) @@ -199,11 +150,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented) @@ -211,11 +158,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInst GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region) @@ -223,11 +166,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented) @@ -235,11 +174,7 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, Region GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index ee65fa8ab0..547b57ee1a 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -908,139 +908,6 @@ TEST_F(StubTest, CheckCast) { #endif } - -TEST_F(StubTest, APutObj) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ - (defined(__x86_64__) && !defined(__APPLE__)) - Thread* self = Thread::Current(); - - // Do not check non-checked ones, we'd need handlers and stuff... - const uintptr_t art_quick_aput_obj_with_null_and_bound_check = - StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck); - - // Create an object - ScopedObjectAccess soa(self); - // garbage is created during ClassLinker::Init - - StackHandleScope<5> hs(soa.Self()); - Handle<mirror::Class> c( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - Handle<mirror::Class> ca( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"))); - - // Build a string array of size 1 - Handle<mirror::ObjectArray<mirror::Object>> array( - hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10))); - - // Build a string -> should be assignable - Handle<mirror::String> str_obj( - hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"))); - - // Build a generic object -> should fail assigning - Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self()))); - - // Play with it... - - // 1) Success cases - // 1.1) Assign str_obj to array[0..3] - - EXPECT_FALSE(self->IsExceptionPending()); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(0)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(1)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(2)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(3)); - - // 1.2) Assign null to array[0..3] - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(0)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(1)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(2)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(3)); - - // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order. - - // 2) Failure cases (str into str[]) - // 2.1) Array = null - // TODO: Throwing NPE needs actual DEX code - -// Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()), -// reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self); -// -// EXPECT_TRUE(self->IsExceptionPending()); -// self->ClearException(); - - // 2.2) Index < 0 - - Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1), - reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // 2.3) Index > 0 - - Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // 3) Failure cases (obj into str[]) - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // Tests done. -#else - LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA; - // Force-print to std::cout so it's also outside the logcat. - std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl; -#endif -} - TEST_F(StubTest, AllocObject) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) @@ -1171,39 +1038,14 @@ TEST_F(StubTest, AllocObjectArray) { ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init - StackHandleScope<2> hs(self); + StackHandleScope<1> hs(self); Handle<mirror::Class> c( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"))); - // Needed to have a linked method. - Handle<mirror::Class> c_obj( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - // Play with it... EXPECT_FALSE(self->IsExceptionPending()); - // For some reason this does not work, as the type_idx is artificial and outside what the - // resolved types of c_obj allow... - - if ((false)) { - // Use an arbitrary method from c to use as referrer - size_t result = Invoke3( - static_cast<size_t>(c->GetDexTypeIndex().index_), // type_idx - 10U, - // arbitrary - reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0, kRuntimePointerSize)), - StubTest::GetEntrypoint(self, kQuickAllocArray), - self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_NE(reinterpret_cast<size_t>(nullptr), result); - mirror::Array* obj = reinterpret_cast<mirror::Array*>(result); - EXPECT_EQ(c.Get(), obj->GetClass()); - VerifyObject(obj); - EXPECT_EQ(obj->GetLength(), 10); - } - { // We can use null in the second argument as we do not need a method here (not used in // resolved/initialized cases) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 62c29cf268..c4202596f2 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -468,7 +468,7 @@ TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThro * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting * of the target Method* in r0 and method->code_ in r1. * - * If unsuccessful, the helper will return null/null will bea pending exception in the + * If unsuccessful, the helper will return null/null and there will be a pending exception in the * thread and we branch to another stub to deliver it. * * On success this wrapper will restore arguments and *jump* to the target, leaving the lr @@ -1352,26 +1352,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax) #endif // USE_READ_BARRIER END_MACRO - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * eax = array, ecx = index, edx = value - */ -DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check - testl %eax, %eax - jnz SYMBOL(art_quick_aput_obj_with_bound_check) - jmp SYMBOL(art_quick_throw_null_pointer_exception) -END_FUNCTION art_quick_aput_obj_with_null_and_bound_check - -DEFINE_FUNCTION art_quick_aput_obj_with_bound_check - movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx - cmpl %ebx, %ecx - jb SYMBOL(art_quick_aput_obj) - mov %ecx, %eax - mov %ebx, %ecx - jmp SYMBOL(art_quick_throw_array_bounds) -END_FUNCTION art_quick_aput_obj_with_bound_check - DEFINE_FUNCTION art_quick_aput_obj test %edx, %edx // store of null jz .Ldo_aput_null @@ -2223,5 +2203,99 @@ DEFINE_FUNCTION art_quick_osr_stub jmp *%ebx END_FUNCTION art_quick_osr_stub +DEFINE_FUNCTION art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame. + mov %esp, %edx // Remember SP. + subl LITERAL(16), %esp // Make space for JValue result. + CFI_ADJUST_CFA_OFFSET(16) + movl LITERAL(0), (%esp) // Initialize result to zero. + movl LITERAL(0), 4(%esp) + mov %esp, %eax // Store pointer to JValue result in eax. + PUSH edx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + PUSH ecx // pass receiver (method handle) + PUSH eax // pass JResult + call SYMBOL(artInvokePolymorphic) // (result, receiver, Thread*, SP) + subl LITERAL('A'), %eax // Eliminate out of bounds options + cmpb LITERAL('Z' - 'A'), %al + ja .Lcleanup_and_return + movzbl %al, %eax + call .Lput_eip_in_ecx +.Lbranch_start: + movl %ecx, %edx + add $(.Lhandler_table - .Lbranch_start), %edx // Make EDX point to handler_table. + leal (%edx, %eax, 2), %eax // Calculate address of entry in table. + movzwl (%eax), %eax // Lookup relative branch in table. + addl %ecx, %eax // Add EIP relative offset. + jmp *%eax // Branch to handler. + + // Handlers for different return types. +.Lstore_boolean_result: + movzbl 16(%esp), %eax // Copy boolean result to the accumulator. + jmp .Lcleanup_and_return +.Lstore_char_result: + movzwl 16(%esp), %eax // Copy char result to the accumulator. + jmp .Lcleanup_and_return +.Lstore_float_result: + movd 16(%esp), %xmm0 // Copy float result to the context restored by + movd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. + jmp .Lcleanup_and_return +.Lstore_double_result: + movsd 16(%esp), %xmm0 // Copy double result to the context restored by + movsd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. + jmp .Lcleanup_and_return +.Lstore_long_result: + movl 20(%esp), %edx // Copy upper-word of result to the context restored by + movl %edx, 72(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. + // Fall-through for lower bits. +.Lstore_int_result: + movl 16(%esp), %eax // Copy int result to the accumulator. + // Fall-through to clean up and return. +.Lcleanup_and_return: + addl LITERAL(32), %esp // Pop arguments and stack allocated JValue result. + CFI_ADJUST_CFA_OFFSET(-32) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION + +.Lput_eip_in_ecx: // Internal function that puts address of + movl 0(%esp), %ecx // next instruction into ECX when CALL + ret + + // Handler table to handlers for given type. +.Lhandler_table: +MACRO1(HANDLER_TABLE_ENTRY, handler_label) + // NB some tools require 16-bits for relocations. Shouldn't need adjusting. + .word RAW_VAR(handler_label) - .Lbranch_start +END_MACRO + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // A + HANDLER_TABLE_ENTRY(.Lstore_int_result) // B (byte) + HANDLER_TABLE_ENTRY(.Lstore_char_result) // C (char) + HANDLER_TABLE_ENTRY(.Lstore_double_result) // D (double) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // E + HANDLER_TABLE_ENTRY(.Lstore_float_result) // F (float) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // G + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // H + HANDLER_TABLE_ENTRY(.Lstore_int_result) // I (int) + HANDLER_TABLE_ENTRY(.Lstore_long_result) // J (long) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // K + HANDLER_TABLE_ENTRY(.Lstore_int_result) // L (object) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // M + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // N + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // O + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // P + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Q + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // R + HANDLER_TABLE_ENTRY(.Lstore_int_result) // S (short) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // T + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // U + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // W + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // X + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Y + HANDLER_TABLE_ENTRY(.Lstore_boolean_result) // Z (boolean) + +END_FUNCTION art_quick_invoke_polymorphic + // TODO: implement these! UNIMPLEMENTED art_quick_memcmp16 diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index facd563428..46bee399f4 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -986,11 +986,7 @@ GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -999,9 +995,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) @@ -1119,12 +1112,11 @@ MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel) END_MACRO // The fast path code for art_quick_alloc_array_region_tlab. -// Inputs: RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* method -// Temps: RCX: the class, r8, r9 +// Inputs: RDI: the class, RSI: int32_t component_count +// Free temps: RCX, RDX, R8, R9 // Output: RAX: return value. MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel) - movq %rcx, %r8 // Save class for later - movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx // Load component type. + movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rdi), %ecx // Load component type. UNPOISON_HEAP_REF ecx movl MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(%rcx), %ecx // Load primitive type. shrq LITERAL(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT), %rcx // Get component size shift. @@ -1151,8 +1143,8 @@ MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel) // Store the class pointer in the // header. // No fence needed for x86. - POISON_HEAP_REF r8d - movl %r8d, MIRROR_OBJECT_CLASS_OFFSET(%rax) + POISON_HEAP_REF edi + movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax) movl %esi, MIRROR_ARRAY_LENGTH_OFFSET(%rax) ret // Fast path succeeded. END_MACRO @@ -1173,8 +1165,8 @@ END_MACRO MACRO1(ALLOC_ARRAY_TLAB_SLOW_PATH, cxx_name) SETUP_SAVE_REFS_ONLY_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() - call CALLVAR(cxx_name) // cxx_name(arg0, arg1, arg2, Thread*) + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() + call CALLVAR(cxx_name) // cxx_name(arg0, arg1, Thread*) RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_MACRO @@ -1199,73 +1191,21 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_tlab ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB END_FUNCTION art_quick_alloc_object_initialized_tlab -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB). -DEFINE_FUNCTION art_quick_alloc_array_tlab - // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* - // RCX: klass, R8, R9: free. RAX: return val. - movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array - movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class - testl %ecx, %ecx - jz .Lart_quick_alloc_array_tlab_slow_path - ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_tlab_slow_path -.Lart_quick_alloc_array_tlab_slow_path: - ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeTLAB -END_FUNCTION art_quick_alloc_array_tlab - // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB). DEFINE_FUNCTION art_quick_alloc_array_resolved_tlab - // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod* - // RCX: mirror::Class* klass, R8, R9: free. RAX: return val. - movq %rdi, %rcx - // Already resolved, no null check. + // RDI: mirror::Class* klass, RSI: int32_t component_count + // RDX, RCX, R8, R9: free. RAX: return val. ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_tlab_slow_path .Lart_quick_alloc_array_resolved_tlab_slow_path: ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedTLAB END_FUNCTION art_quick_alloc_array_resolved_tlab -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB). -DEFINE_FUNCTION art_quick_alloc_array_region_tlab - // Fast path region tlab allocation. - // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* - // RCX: klass, R8, R9: free. RAX: return val. - ASSERT_USE_READ_BARRIER - movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array - movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class - // Null check so that we can load the lock word. - testl %ecx, %ecx - jz .Lart_quick_alloc_array_region_tlab_slow_path - // Since we have allocation entrypoint switching, we know the GC is marking. - // Check the mark bit, if it is 0, do the read barrier mark. - testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx) - jz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path -.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit: - ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_region_tlab_slow_path -.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path: - // The read barrier slow path. Mark the class. - PUSH rdi - PUSH rsi - PUSH rdx - // Outgoing argument set up - movq %rcx, %rdi // Pass the class as the first param. - call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj) - movq %rax, %rcx - POP rdx - POP rsi - POP rdi - jmp .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit -.Lart_quick_alloc_array_region_tlab_slow_path: - ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeRegionTLAB -END_FUNCTION art_quick_alloc_array_region_tlab - // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB). DEFINE_FUNCTION art_quick_alloc_array_resolved_region_tlab // Fast path region tlab allocation. - // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod* - // RCX: mirror::Class* klass, R8, R9: free. RAX: return val. + // RDI: mirror::Class* klass, RSI: int32_t component_count + // RCX, RDX, R8, R9: free. RAX: return val. ASSERT_USE_READ_BARRIER - movq %rdi, %rcx - // Caller is responsible for read barrier. - // Already resolved, no null check. ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_region_tlab_slow_path .Lart_quick_alloc_array_resolved_region_tlab_slow_path: ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB @@ -1466,7 +1406,7 @@ END_MACRO * 64b PUSH/POP and 32b argument. * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. * - * As with art_quick_aput_obj* functions, the 64b versions are in comments. + * As with art_quick_aput_obj function, the 64b versions are in comments. */ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #ifdef USE_READ_BARRIER @@ -1503,46 +1443,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #endif // USE_READ_BARRIER END_MACRO - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * - * Currently all the parameters should fit into the 32b portions of the registers. Index always - * will. So we optimize for a tighter encoding. The 64b versions are in comments. - * - * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value - */ -DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check -#if defined(__APPLE__) - int3 - int3 -#else - testl %edi, %edi -// testq %rdi, %rdi - jnz art_quick_aput_obj_with_bound_check - jmp art_quick_throw_null_pointer_exception -#endif // __APPLE__ -END_FUNCTION art_quick_aput_obj_with_null_and_bound_check - - -DEFINE_FUNCTION art_quick_aput_obj_with_bound_check -#if defined(__APPLE__) - int3 - int3 -#else - movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx -// movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx) - cmpl %ecx, %esi - jb art_quick_aput_obj - mov %esi, %edi -// mov %rsi, %rdi - mov %ecx, %esi -// mov %rcx, %rsi - jmp art_quick_throw_array_bounds -#endif // __APPLE__ -END_FUNCTION art_quick_aput_obj_with_bound_check - - DEFINE_FUNCTION art_quick_aput_obj testl %edx, %edx // store of null // test %rdx, %rdx @@ -2394,3 +2294,79 @@ DEFINE_FUNCTION art_quick_osr_stub rep movsb // while (rcx--) { *rdi++ = *rsi++ } jmp *%rdx END_FUNCTION art_quick_osr_stub + +DEFINE_FUNCTION art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread + movq %rsp, %rcx // pass SP + subq LITERAL(16), %rsp // make space for JValue result + CFI_ADJUST_CFA_OFFSET(16) + movq LITERAL(0), (%rsp) // initialize result + movq %rsp, %rdi // store pointer to JValue result + call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP) + // save the code pointer + subq LITERAL('A'), %rax // Convert type descriptor character value to a zero based index. + cmpb LITERAL('Z' - 'A'), %al // Eliminate out of bounds options + ja .Lcleanup_and_return + movzbq %al, %rax + leaq .Lhandler_table(%rip), %rcx // Get the address of the handler table + movslq (%rcx, %rax, 4), %rax // Lookup handler offset relative to table + addq %rcx, %rax // Add table address to yield handler address. + jmpq *%rax // Jump to handler. + +.align 4 +.Lhandler_table: // Table of type descriptor to handlers. +MACRO1(HANDLER_TABLE_OFFSET, handle_label) + // NB some tools require 32-bits for relocations. Shouldn't need adjusting. + .long RAW_VAR(handle_label) - .Lhandler_table +END_MACRO + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A + HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte) + HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) + HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E + HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H + HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int) + HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K + HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R + HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y + HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) + +.Lstore_boolean_result: + movzbq (%rsp), %rax // Copy boolean result to the accumulator + jmp .Lcleanup_and_return +.Lstore_char_result: + movzwq (%rsp), %rax // Copy char result to the accumulator + jmp .Lcleanup_and_return +.Lstore_float_result: + movd (%rsp), %xmm0 // Copy float result to the context restored by + movd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME. + jmp .Lcleanup_and_return +.Lstore_double_result: + movsd (%rsp), %xmm0 // Copy double result to the context restored by + movsd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME. + jmp .Lcleanup_and_return +.Lstore_long_result: + movq (%rsp), %rax // Copy long result to the accumulator. + // Fall-through +.Lcleanup_and_return: + addq LITERAL(16), %rsp // Pop space for JValue result. + CFI_ADJUST_CFA_OFFSET(16) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION +END_FUNCTION art_quick_invoke_polymorphic diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 96976d9bce..15938c52be 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -121,7 +121,7 @@ inline uint16_t ArtMethod::GetMethodIndexDuringLinking() { inline uint32_t ArtMethod::GetDexMethodIndex() { DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || GetDeclaringClass()->IsErroneous()); - return dex_method_index_; + return GetDexMethodIndexUnchecked(); } inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) { @@ -175,47 +175,15 @@ inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerS other->GetDexCacheResolvedMethods(pointer_size); } -inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(PointerSize pointer_size) { - return GetNativePointer<GcRoot<mirror::Class>*>(DexCacheResolvedTypesOffset(pointer_size), - pointer_size); -} - -template <bool kWithCheck> -inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index, - PointerSize pointer_size) { - if (kWithCheck) { - mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache(); - if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) { - ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes()); - return nullptr; - } - } - mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read(); - return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr; -} - -inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) { - return GetDexCacheResolvedTypes(pointer_size) != nullptr; -} - -inline bool ArtMethod::HasSameDexCacheResolvedTypes(GcRoot<mirror::Class>* other_cache, - PointerSize pointer_size) { - return GetDexCacheResolvedTypes(pointer_size) == other_cache; -} - -inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, PointerSize pointer_size) { - return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size); -} - -inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, - bool resolve, - PointerSize pointer_size) { - mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); +inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) { + ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); + ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); + if (UNLIKELY(type == nullptr) && resolve) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + type = class_linker->ResolveType(type_idx, this); CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); } - return type; + return type.Ptr(); } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -333,9 +301,9 @@ inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { return GetDexFile()->GetCodeItem(GetCodeItemOffset()); } -inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) { +inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) { DCHECK(!IsProxyMethod()); - return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr; + return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr; } inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) { @@ -430,23 +398,13 @@ inline void ArtMethod::SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_meth pointer_size); } -inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, - PointerSize pointer_size) { - SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size); -} - -inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) { +inline mirror::Class* ArtMethod::GetReturnType(bool resolve) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); dex::TypeIndex return_type_idx = proto_id.return_type_idx_; - mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type; + return GetClassFromTypeIndex(return_type_idx, resolve); } inline bool ArtMethod::HasSingleImplementation() { @@ -530,11 +488,6 @@ inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor, if (old_methods != new_methods) { SetDexCacheResolvedMethods(new_methods, pointer_size); } - GcRoot<mirror::Class>* old_types = GetDexCacheResolvedTypes(pointer_size); - GcRoot<mirror::Class>* new_types = visitor(old_types); - if (old_types != new_types) { - SetDexCacheResolvedTypes(new_types, pointer_size); - } } template <ReadBarrierOption kReadBarrierOption, typename Visitor> diff --git a/runtime/art_method.cc b/runtime/art_method.cc index dfc7837aea..d7d39afa8f 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -236,7 +236,6 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, // Default to handler not found. uint32_t found_dex_pc = DexFile::kDexNoIndex; // Iterate over the catch handlers associated with dex_pc. - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) { dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex(); // Catch all case @@ -245,9 +244,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, break; } // Does this catch exception type apply? - mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, - true /* resolve */, - pointer_size); + mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */); if (UNLIKELY(iter_exception_type == nullptr)) { // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was // removed by a pro-guard like tool. diff --git a/runtime/art_method.h b/runtime/art_method.h index 11dcc35df5..17f343d442 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -131,12 +131,12 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccStatic) != 0; } - // Returns true if the method is a constructor. + // Returns true if the method is a constructor according to access flags. bool IsConstructor() { return (GetAccessFlags() & kAccConstructor) != 0; } - // Returns true if the method is a class initializer. + // Returns true if the method is a class initializer according to access flags. bool IsClassInitializer() { return IsConstructor() && IsStatic(); } @@ -322,6 +322,9 @@ class ArtMethod FINAL { // Number of 32bit registers that would be required to hold all the arguments static size_t NumArgRegisters(const StringPiece& shorty); + ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() { + return dex_method_index_; + } ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { @@ -348,22 +351,8 @@ class ArtMethod FINAL { bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - template <bool kWithCheck = true> - mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - bool HasDexCacheResolvedTypes(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - bool HasSameDexCacheResolvedTypes(ArtMethod* other, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - bool HasSameDexCacheResolvedTypes(GcRoot<mirror::Class>* other_cache, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - // Get the Class* from the type index into this method's dex cache. - mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, - bool resolve, - PointerSize pointer_size) + mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if this method has the same name and signature of the other method. @@ -414,12 +403,6 @@ class ArtMethod FINAL { * static_cast<size_t>(pointer_size)); } - static MemberOffset DexCacheResolvedTypesOffset(PointerSize pointer_size) { - return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER( - PtrSizedFields, dex_cache_resolved_types_) / sizeof(void*) - * static_cast<size_t>(pointer_size)); - } - static MemberOffset DataOffset(PointerSize pointer_size) { return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER( PtrSizedFields, data_) / sizeof(void*) * static_cast<size_t>(pointer_size)); @@ -555,8 +538,7 @@ class ArtMethod FINAL { const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); - bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); @@ -577,8 +559,7 @@ class ArtMethod FINAL { // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large // number of bugs at call sites. - mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_); mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); @@ -608,9 +589,6 @@ class ArtMethod FINAL { void CopyFrom(ArtMethod* src, PointerSize image_pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ALWAYS_INLINE GcRoot<mirror::Class>* GetDexCacheResolvedTypes(PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - // Note, hotness_counter_ updates are non-atomic but it doesn't need to be precise. Also, // given that the counter is only 16 bits wide we can expect wrap-around in some // situations. Consumers of hotness_count_ must be able to deal with that. @@ -657,8 +635,6 @@ class ArtMethod FINAL { std::string JniLongName() REQUIRES_SHARED(Locks::mutator_lock_); - - // Update heap objects and non-entrypoint pointers by the passed in visitor for image relocation. // Does not use read barrier. template <typename Visitor> @@ -707,9 +683,6 @@ class ArtMethod FINAL { // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access. ArtMethod** dex_cache_resolved_methods_; - // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access. - GcRoot<mirror::Class>* dex_cache_resolved_types_; - // Pointer to JNI function registered to this method, or a function to resolve the JNI function, // or the profiling data for non-native methods, or an ImtConflictTable. void* data_; diff --git a/runtime/asm_support.h b/runtime/asm_support.h index bfdddf7b03..ed83f1c57f 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -90,7 +90,7 @@ ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 198 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 34 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. @@ -101,8 +101,10 @@ ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, #define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value()) + // Offset of field Thread::tlsPtr_.mterp_current_ibase. -#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__) +#define THREAD_CURRENT_IBASE_OFFSET \ + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 157) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 9116097604..e05a85a116 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -46,6 +46,7 @@ Mutex* Locks::deoptimization_lock_ = nullptr; ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr; Mutex* Locks::instrument_entrypoints_lock_ = nullptr; Mutex* Locks::intern_table_lock_ = nullptr; +Mutex* Locks::jni_function_table_lock_ = nullptr; Mutex* Locks::jni_libraries_lock_ = nullptr; Mutex* Locks::logging_lock_ = nullptr; Mutex* Locks::mem_maps_lock_ = nullptr; @@ -957,6 +958,7 @@ void Locks::Init() { DCHECK(verifier_deps_lock_ != nullptr); DCHECK(host_dlopen_handles_lock_ != nullptr); DCHECK(intern_table_lock_ != nullptr); + DCHECK(jni_function_table_lock_ != nullptr); DCHECK(jni_libraries_lock_ != nullptr); DCHECK(logging_lock_ != nullptr); DCHECK(mutator_lock_ != nullptr); @@ -1098,6 +1100,10 @@ void Locks::Init() { DCHECK(jni_weak_globals_lock_ == nullptr); jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock); + DCHECK(jni_function_table_lock_ == nullptr); + jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kAbortLock); DCHECK(abort_lock_ == nullptr); abort_lock_ = new Mutex("abort lock", current_lock_level, true); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 2adeb8cc97..21dd437711 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -68,6 +68,7 @@ enum LockLevel { kRosAllocBulkFreeLock, kMarkSweepMarkStackLock, kTransactionLogLock, + kJniFunctionTableLock, kJniWeakGlobalsLock, kJniGlobalsLock, kReferenceQueueSoftReferencesLock, @@ -698,8 +699,11 @@ class Locks { // Guard accesses to the JNI Weak Global Reference table. static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_); + // Guard accesses to the JNI function table override. + static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_); + // Have an exclusive aborting thread. - static Mutex* abort_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_); + static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_); // Allow mutual exclusion when manipulating Thread::suspend_count_. // TODO: Does the trade-off of a per-thread lock make sense? diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 5fc5f1a2f5..2e17dd85e6 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -25,7 +25,6 @@ #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" -#include "mirror/throwable.h" #include "mirror/object_array.h" #include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" @@ -90,25 +89,16 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr<mirror::Class> resolved_type = - referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_); + ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); - // There could be an out of bounds exception from GetDexCacheResolvedType, don't call - // ResolveType for this case. - if (LIKELY(!hs.Self()->IsExceptionPending())) { - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); - // Note: We cannot check here to see whether we added the type to the cache. The type - // might be an erroneous class, which results in it being hidden from us. - } else { - // Make sure its an array out of bounds exception. - DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals( - "Ljava/lang/ArrayIndexOutOfBoundsException;")); - } + ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + const DexFile& dex_file = *dex_cache->GetDexFile(); + resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + // Note: We cannot check here to see whether we added the type to the cache. The type + // might be an erroneous class, which results in it being hidden from us. } return resolved_type.Ptr(); } @@ -256,8 +246,8 @@ ArtMethod* ClassLinker::FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, // Locate the dex cache of the original interface/Object for (const DexCacheData& data : dex_caches_) { if (!self->IsJWeakCleared(data.weak_root) && - proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types, - image_pointer_size_)) { + proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, + image_pointer_size_)) { ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); if (dex_cache != nullptr) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 035ceadeb7..14918df4d5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -65,7 +65,7 @@ #include "interpreter/interpreter.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "jni_internal.h" #include "leb128.h" #include "linear_alloc.h" @@ -96,6 +96,7 @@ #include "object_lock.h" #include "os.h" #include "runtime.h" +#include "runtime_callbacks.h" #include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -351,7 +352,7 @@ ClassLinker::ClassLinker(InternTable* intern_table) array_iftable_(nullptr), find_array_class_cache_next_victim_(0), init_done_(false), - log_new_class_table_roots_(false), + log_new_roots_(false), intern_table_(intern_table), quick_resolution_trampoline_(nullptr), quick_imt_conflict_trampoline_(nullptr), @@ -1099,23 +1100,7 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { explicit FixupArtMethodArrayVisitor(const ImageHeader& header) : header_(header) {} virtual void Visit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(kRuntimePointerSize); const bool is_copied = method->IsCopied(); - if (resolved_types != nullptr) { - bool in_image_space = false; - if (kIsDebugBuild || is_copied) { - in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( - reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin()); - } - // Must be in image space for non-miranda method. - DCHECK(is_copied || in_image_space) - << resolved_types << " is not in image starting at " - << reinterpret_cast<void*>(header_.GetImageBegin()); - if (!is_copied || in_image_space) { - method->SetDexCacheResolvedTypes(method->GetDexCache()->GetResolvedTypes(), - kRuntimePointerSize); - } - } ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(kRuntimePointerSize); if (resolved_methods != nullptr) { bool in_image_space = false; @@ -1865,12 +1850,10 @@ bool ClassLinker::AddImageSpace( << reinterpret_cast<const void*>(section_end); } } - if (!oat_file->GetBssGcRoots().empty()) { - // Insert oat file to class table for visiting .bss GC roots. - class_table->InsertOatFile(oat_file); - } - } else { - DCHECK(oat_file->GetBssGcRoots().empty()); + } + if (!oat_file->GetBssGcRoots().empty()) { + // Insert oat file to class table for visiting .bss GC roots. + class_table->InsertOatFile(oat_file); } if (added_class_table) { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); @@ -1934,14 +1917,27 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { // Concurrent moving GC marked new roots through the to-space invariant. CHECK_EQ(new_ref, old_ref); } + for (const OatFile* oat_file : new_bss_roots_boot_oat_files_) { + for (GcRoot<mirror::Object>& root : oat_file->GetBssGcRoots()) { + ObjPtr<mirror::Object> old_ref = root.Read<kWithoutReadBarrier>(); + if (old_ref != nullptr) { + DCHECK(old_ref->IsClass()); + root.VisitRoot(visitor, RootInfo(kRootStickyClass)); + ObjPtr<mirror::Object> new_ref = root.Read<kWithoutReadBarrier>(); + // Concurrent moving GC marked new roots through the to-space invariant. + CHECK_EQ(new_ref, old_ref); + } + } + } } if ((flags & kVisitRootFlagClearRootLog) != 0) { new_class_roots_.clear(); + new_bss_roots_boot_oat_files_.clear(); } if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { - log_new_class_table_roots_ = true; + log_new_roots_ = true; } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { - log_new_class_table_roots_ = false; + log_new_roots_ = false; } // We deliberately ignore the class roots in the image since we // handle image roots by using the MS/CMS rescanning of dirty cards. @@ -2683,6 +2679,11 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, return nullptr; } CHECK(klass->IsLoaded()); + + // At this point the class is loaded. Publish a ClassLoad even. + // Note: this may be a temporary class. It is a listener's responsibility to handle this. + Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass); + // Link the class (if necessary) CHECK(!klass->IsResolved()); // TODO: Use fast jobjects? @@ -2700,10 +2701,6 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, CHECK(h_new_class.Get() != nullptr) << descriptor; CHECK(h_new_class->IsResolved()) << descriptor; - // Update the dex cache of where the class is defined. Inlining depends on having - // this filled. - h_new_class->GetDexCache()->SetResolvedType(h_new_class->GetDexTypeIndex(), h_new_class.Get()); - // Instrumentation may have updated entrypoints for all methods of all // classes. However it could not update methods of this class while we // were loading it. Now the class is resolved, we can update entrypoints @@ -2727,7 +2724,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, * The class has been prepared and resolved but possibly not yet verified * at this point. */ - Dbg::PostClassPrepare(h_new_class.Get()); + Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class); // Notify native debugger of the new class and its layout. jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get()); @@ -3200,7 +3197,6 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_); - dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes(), image_pointer_size_); uint32_t access_flags = it.GetMethodAccessFlags(); @@ -3296,7 +3292,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, DexCacheData data; data.weak_root = dex_cache_jweak; data.dex_file = dex_cache->GetDexFile(); - data.resolved_types = dex_cache->GetResolvedTypes(); + data.resolved_methods = dex_cache->GetResolvedMethods(); dex_caches_.push_back(data); } @@ -3307,6 +3303,7 @@ mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file, ReaderMutexLock mu(self, *Locks::dex_lock_); ObjPtr<mirror::DexCache> dex_cache = FindDexCacheLocked(self, dex_file, true); if (dex_cache != nullptr) { + // TODO: Check if the dex file was registered with the same class loader. Bug: 34193123 return dex_cache.Ptr(); } } @@ -3651,7 +3648,7 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr<mirror::C // This is necessary because we need to have the card dirtied for remembered sets. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); } - if (log_new_class_table_roots_) { + if (log_new_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } } @@ -3664,6 +3661,14 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr<mirror::C return nullptr; } +void ClassLinker::WriteBarrierForBootOatFileBssRoots(const OatFile* oat_file) { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation(); + if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) { + new_bss_roots_boot_oat_files_.push_back(oat_file); + } +} + // TODO This should really be in mirror::Class. void ClassLinker::UpdateClassMethods(ObjPtr<mirror::Class> klass, LengthPrefixedArray<ArtMethod>* new_methods) { @@ -4349,7 +4354,6 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons // The proxy method doesn't have its own dex cache or dex file and so it steals those of its // interface prototype. The exception to this are Constructors and the Class of the Proxy itself. CHECK(prototype->HasSameDexCacheResolvedMethods(method, image_pointer_size_)); - CHECK(prototype->HasSameDexCacheResolvedTypes(method, image_pointer_size_)); auto* np = method->GetInterfaceMethodIfProxy(image_pointer_size_); CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), np->GetDexCache()); CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex()); @@ -4357,8 +4361,7 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons CHECK_STREQ(np->GetName(), prototype->GetName()); CHECK_STREQ(np->GetShorty(), prototype->GetShorty()); // More complex sanity - via dex cache - CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_), - prototype->GetReturnType(true /* resolve */, image_pointer_size_)); + CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */)); } bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics, @@ -4820,7 +4823,6 @@ static void ThrowSignatureMismatch(Handle<mirror::Class> klass, } static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, - PointerSize pointer_size, Handle<mirror::Class> klass, Handle<mirror::Class> super_klass, ArtMethod* method1, @@ -4828,14 +4830,12 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { { StackHandleScope<1> hs(self); - Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */, - pointer_size))); + Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */))); if (UNLIKELY(return_type.Get() == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1); return false; } - ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */, - pointer_size); + ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */); if (UNLIKELY(other_return_type == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2); return false; @@ -4880,7 +4880,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, StackHandleScope<1> hs(self); dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_; Handle<mirror::Class> param_type(hs.NewHandle( - method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size))); + method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */))); if (UNLIKELY(param_type.Get() == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method1, i, param_type_idx); @@ -4888,7 +4888,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, } dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_; ObjPtr<mirror::Class> other_param_type = - method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size); + method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */); if (UNLIKELY(other_param_type == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method2, i, other_param_type_idx); @@ -4924,9 +4924,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { auto* m = klass->GetVTableEntry(i, image_pointer_size_); auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_); if (m != super_m) { - if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_, - klass, super_klass, - m, super_m))) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { self->AssertPendingException(); return false; } @@ -4942,9 +4944,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { j, image_pointer_size_); auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_); if (m != super_m) { - if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_, - klass, super_klass, - m, super_m))) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { self->AssertPendingException(); return false; } @@ -5161,7 +5165,7 @@ bool ClassLinker::LinkClass(Thread* self, Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); } CHECK_EQ(existing, klass.Get()); - if (log_new_class_table_roots_) { + if (log_new_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(h_new_class.Get())); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 77322ede08..8da979b36f 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -34,6 +34,7 @@ #include "dex_file.h" #include "dex_file_types.h" #include "gc_root.h" +#include "handle.h" #include "jni.h" #include "mirror/class.h" #include "object_callbacks.h" @@ -64,6 +65,7 @@ class ImtConflictTable; template<typename T> class LengthPrefixedArray; template<class T> class MutableHandle; class InternTable; +class OatFile; template<class T> class ObjectLock; class Runtime; class ScopedObjectAccessAlreadyRunnable; @@ -535,6 +537,12 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Add an oat file with .bss GC roots to be visited again at the end of GC + // for collector types that need it. + void WriteBarrierForBootOatFileBssRoots(const OatFile* oat_file) + REQUIRES(!Locks::classlinker_classes_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Class>* GetClassRoots() REQUIRES_SHARED(Locks::mutator_lock_) { mirror::ObjectArray<mirror::Class>* class_roots = class_roots_.Read(); DCHECK(class_roots != nullptr); @@ -638,6 +646,14 @@ class ClassLinker { mirror::Class* GetHoldingClassOfCopiedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns null if not found. + ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + + void AppendToBootClassPath(Thread* self, const DexFile& dex_file) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. @@ -646,7 +662,7 @@ class ClassLinker { // jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with // class unloading.) const DexFile* dex_file; - GcRoot<mirror::Class>* resolved_types; + ArtMethod** resolved_methods; }; private: @@ -733,9 +749,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - void AppendToBootClassPath(Thread* self, const DexFile& dex_file) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::dex_lock_); void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); @@ -1032,10 +1045,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_); - // Returns null if not found. - ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - // Insert a new class table if not found. ClassTable* InsertClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) @@ -1138,6 +1147,10 @@ class ClassLinker { // New class roots, only used by CMS since the GC needs to mark these in the pause. std::vector<GcRoot<mirror::Class>> new_class_roots_ GUARDED_BY(Locks::classlinker_classes_lock_); + // Boot image oat files with new .bss GC roots to be visited in the pause by CMS. + std::vector<const OatFile*> new_bss_roots_boot_oat_files_ + GUARDED_BY(Locks::classlinker_classes_lock_); + // Number of times we've searched dex caches for a class. After a certain number of misses we move // the classes into the class_table_ to avoid dex cache based searches. Atomic<uint32_t> failed_dex_cache_class_lookups_; @@ -1155,7 +1168,7 @@ class ClassLinker { size_t find_array_class_cache_next_victim_; bool init_done_; - bool log_new_class_table_roots_ GUARDED_BY(Locks::classlinker_classes_lock_); + bool log_new_roots_ GUARDED_BY(Locks::classlinker_classes_lock_); InternTable* intern_table_; @@ -1182,6 +1195,21 @@ class ClassLinker { DISALLOW_COPY_AND_ASSIGN(ClassLinker); }; +class ClassLoadCallback { + public: + virtual ~ClassLoadCallback() {} + + // 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. + virtual void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + // A class has been prepared, i.e., resolved. As the ClassLoad event might have been for a + // temporary class, provide both the former and the current class. + virtual void ClassPrepare(Handle<mirror::Class> temp_klass, + Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + } // namespace art #endif // ART_RUNTIME_CLASS_LINKER_H_ diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 42108d8621..d98daa51fe 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -242,13 +242,9 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(method->GetSignature() != Signature::NoSignature()); EXPECT_TRUE(method->HasDexCacheResolvedMethods(kRuntimePointerSize)); - EXPECT_TRUE(method->HasDexCacheResolvedTypes(kRuntimePointerSize)); EXPECT_TRUE(method->HasSameDexCacheResolvedMethods( method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods(), kRuntimePointerSize)); - EXPECT_TRUE(method->HasSameDexCacheResolvedTypes( - method->GetDeclaringClass()->GetDexCache()->GetResolvedTypes(), - kRuntimePointerSize)); } void AssertField(ObjPtr<mirror::Class> klass, ArtField* field) @@ -460,7 +456,6 @@ class ClassLinkerMethodHandlesTest : public ClassLinkerTest { protected: virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonRuntimeTest::SetUpRuntimeOptions(options); - options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr)); } }; @@ -617,7 +612,7 @@ struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") { addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods"); - addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_cache_), "originalDexCache"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_bytes_), "originalDexFile"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError"); } }; @@ -757,6 +752,7 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> { EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>( false, "Ldalvik/system/EmulatedStackFrame;") { + addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, callsite_type_), "callsiteType"); addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, references_), "references"); addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, stack_frame_), "stackFrame"); addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, type_), "type"); @@ -899,7 +895,6 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); const DexFile& dex_file = klass->GetDexFile(); - EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), klass); diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 743fcc87eb..fc82264b6a 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -133,7 +133,9 @@ void ScratchFile::Unlink() { static bool unstarted_initialized_ = false; -CommonRuntimeTestImpl::CommonRuntimeTestImpl() {} +CommonRuntimeTestImpl::CommonRuntimeTestImpl() + : class_linker_(nullptr), java_lang_dex_file_(nullptr) { +} CommonRuntimeTestImpl::~CommonRuntimeTestImpl() { // Ensure the dex files are cleaned up before the runtime. @@ -425,7 +427,9 @@ void CommonRuntimeTestImpl::TearDown() { TearDownAndroidData(android_data_, true); dalvik_cache_.clear(); - Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test + if (runtime_ != nullptr) { + runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption after the test + } } static std::string GetDexFileName(const std::string& jar_prefix, bool host) { diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index c30272e114..a44f79e193 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -428,6 +428,8 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru case Instruction::INVOKE_VIRTUAL_RANGE: case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: + case Instruction::INVOKE_POLYMORPHIC: + case Instruction::INVOKE_POLYMORPHIC_RANGE: case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { // Without inlining, we could just check that the offset is the class offset. @@ -551,6 +553,12 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::INVOKE_INTERFACE_RANGE: ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kInterface); break; + case Instruction::INVOKE_POLYMORPHIC: + ThrowNullPointerExceptionForMethodAccess(instr->VRegB_45cc(), kVirtual); + break; + case Instruction::INVOKE_POLYMORPHIC_RANGE: + ThrowNullPointerExceptionForMethodAccess(instr->VRegB_4rcc(), kVirtual); + break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { // Since we replaced the method index, we ask the verifier to tell us which diff --git a/runtime/debugger.cc b/runtime/debugger.cc index df4413d52c..22a31635a6 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -320,6 +320,9 @@ size_t Dbg::field_write_event_ref_count_ = 0; size_t Dbg::exception_catch_event_ref_count_ = 0; uint32_t Dbg::instrumentation_events_ = 0; +Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_; +Dbg::DbgClassLoadCallback Dbg::class_load_callback_; + // Breakpoints. static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); @@ -3985,9 +3988,7 @@ JDWP::JdwpError Dbg::PrepareInvokeMethod(uint32_t request_id, JDWP::ObjectId thr if (shorty[i + 1] == 'L') { // Did we really get an argument of an appropriate reference type? mirror::Class* parameter_type = - m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, - true /* resolve */, - kRuntimePointerSize); + m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */); mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error); if (error != JDWP::ERR_NONE) { return JDWP::ERR_INVALID_OBJECT; @@ -5137,4 +5138,20 @@ void Dbg::VisitRoots(RootVisitor* visitor) { } } +void Dbg::DbgThreadLifecycleCallback::ThreadStart(Thread* self) { + Dbg::PostThreadStart(self); +} + +void Dbg::DbgThreadLifecycleCallback::ThreadDeath(Thread* self) { + Dbg::PostThreadDeath(self); +} + +void Dbg::DbgClassLoadCallback::ClassLoad(Handle<mirror::Class> klass ATTRIBUTE_UNUSED) { + // Ignore ClassLoad; +} +void Dbg::DbgClassLoadCallback::ClassPrepare(Handle<mirror::Class> temp_klass ATTRIBUTE_UNUSED, + Handle<mirror::Class> klass) { + Dbg::PostClassPrepare(klass.Get()); +} + } // namespace art diff --git a/runtime/debugger.h b/runtime/debugger.h index 3b4a5e16b0..a7fd1605df 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -28,6 +28,8 @@ #include <vector> #include "gc_root.h" +#include "class_linker.h" +#include "handle.h" #include "jdwp/jdwp.h" #include "jni.h" #include "jvalue.h" @@ -502,12 +504,6 @@ class Dbg { REQUIRES_SHARED(Locks::mutator_lock_); static void PostException(mirror::Throwable* exception) REQUIRES_SHARED(Locks::mutator_lock_); - static void PostThreadStart(Thread* t) - REQUIRES_SHARED(Locks::mutator_lock_); - static void PostThreadDeath(Thread* t) - REQUIRES_SHARED(Locks::mutator_lock_); - static void PostClassPrepare(mirror::Class* c) - REQUIRES_SHARED(Locks::mutator_lock_); static void UpdateDebugger(Thread* thread, mirror::Object* this_object, ArtMethod* method, uint32_t new_dex_pc, @@ -707,6 +703,13 @@ class Dbg { return instrumentation_events_; } + static ThreadLifecycleCallback* GetThreadLifecycleCallback() { + return &thread_lifecycle_callback_; + } + static ClassLoadCallback* GetClassLoadCallback() { + return &class_load_callback_; + } + private: static void ExecuteMethodWithoutPendingException(ScopedObjectAccess& soa, DebugInvokeReq* pReq) REQUIRES_SHARED(Locks::mutator_lock_); @@ -725,9 +728,17 @@ class Dbg { REQUIRES(!Locks::thread_list_lock_) REQUIRES_SHARED(Locks::mutator_lock_); static void DdmBroadcast(bool connect) REQUIRES_SHARED(Locks::mutator_lock_); + + static void PostThreadStart(Thread* t) + REQUIRES_SHARED(Locks::mutator_lock_); + static void PostThreadDeath(Thread* t) + REQUIRES_SHARED(Locks::mutator_lock_); static void PostThreadStartOrStop(Thread*, uint32_t) REQUIRES_SHARED(Locks::mutator_lock_); + static void PostClassPrepare(mirror::Class* c) + REQUIRES_SHARED(Locks::mutator_lock_); + static void PostLocationEvent(ArtMethod* method, int pcOffset, mirror::Object* thisPtr, int eventFlags, const JValue* return_value) @@ -789,6 +800,22 @@ class Dbg { static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_); static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_); + class DbgThreadLifecycleCallback : public ThreadLifecycleCallback { + public: + void ThreadStart(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + void ThreadDeath(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + }; + + class DbgClassLoadCallback : public ClassLoadCallback { + public: + void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + void ClassPrepare(Handle<mirror::Class> temp_klass, + Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + }; + + static DbgThreadLifecycleCallback thread_lifecycle_callback_; + static DbgClassLoadCallback class_load_callback_; + DISALLOW_COPY_AND_ASSIGN(Dbg); }; diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index b0c4597d73..7ae9f03c83 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -160,7 +160,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { // image at GetImageLocation(). This is used for testing mismatched // image checksums in the oat_file_assistant_tests. std::string GetImageLocation2() const { - return GetImageDirectory() + "/core-npic.art"; + return GetImageDirectory() + "/core-interpreter.art"; } std::string GetDexSrc1() const { diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 7d704ad0cc..f59420d332 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -1274,6 +1274,16 @@ std::string Signature::ToString() const { return result; } +uint32_t Signature::GetNumberOfParameters() const { + const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); + return (params != nullptr) ? params->Size() : 0; +} + +bool Signature::IsVoid() const { + const char* return_type = dex_file_->GetReturnTypeDescriptor(*proto_id_); + return strcmp(return_type, "V") == 0; +} + bool Signature::operator==(const StringPiece& rhs) const { if (dex_file_ == nullptr) { return false; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 250795b21d..cb7f174787 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1197,6 +1197,9 @@ class Signature : public ValueObject { return Signature(); } + bool IsVoid() const; + uint32_t GetNumberOfParameters() const; + bool operator==(const Signature& rhs) const; bool operator!=(const Signature& rhs) const { return !(*this == rhs); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 9504e8bd29..16a447b0a6 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -608,7 +608,7 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, return nullptr; } Handle<mirror::Class> method_return(hs.NewHandle( - annotation_method->GetReturnType(true /* resolve */, pointer_size))); + annotation_method->GetReturnType(true /* resolve */))); DexFile::AnnotationValue annotation_value; if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, @@ -948,9 +948,7 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { DexFile::AnnotationValue annotation_value; StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Class> h_klass(hs.NewHandle(klass)); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - Handle<mirror::Class> return_type(hs.NewHandle( - method->GetReturnType(true /* resolve */, pointer_size))); + Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */))); if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, DexFile::kAllObjects)) { return nullptr; diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index a3ab9fa5bd..318123edcd 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -91,6 +91,66 @@ const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const ch return dex_file_->StringDataByIdx(idx); } +// Try to find the name of the method with the given index. We do not want to rely on DexFile +// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and +// header_ of the DexFileVerifier. str will contain the pointer to the method name on success +// (flagged by the return value), otherwise error_msg will contain an error string. +static bool FindMethodName(uint32_t method_index, + const uint8_t* begin, + const DexFile::Header* header, + const char** str, + std::string* error_msg) { + if (method_index >= header->method_ids_size_) { + *error_msg = "Method index not available for method flags verification"; + return false; + } + uint32_t string_idx = + (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + + method_index)->name_idx_.index_; + if (string_idx >= header->string_ids_size_) { + *error_msg = "String index not available for method flags verification"; + return false; + } + uint32_t string_off = + (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)-> + string_data_off_; + if (string_off >= header->file_size_) { + *error_msg = "String offset out of bounds for method flags verification"; + return false; + } + const uint8_t* str_data_ptr = begin + string_off; + uint32_t dummy; + if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) { + *error_msg = "String size out of bounds for method flags verification"; + return false; + } + *str = reinterpret_cast<const char*>(str_data_ptr); + return true; +} + +// Gets constructor flags based on the |method_name|. Returns true if +// method_name is either <clinit> or <init> and sets +// |constructor_flags_by_name| appropriately. Otherwise set +// |constructor_flags_by_name| to zero and returns whether +// |method_name| is valid. +bool GetConstructorFlagsForMethodName(const char* method_name, + uint32_t* constructor_flags_by_name) { + if (method_name[0] != '<') { + *constructor_flags_by_name = 0; + return true; + } + if (strcmp(method_name + 1, "clinit>") == 0) { + *constructor_flags_by_name = kAccStatic | kAccConstructor; + return true; + } + if (strcmp(method_name + 1, "init>") == 0) { + *constructor_flags_by_name = kAccConstructor; + return true; + } + *constructor_flags_by_name = 0; + return false; +} + const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_string) { if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) { @@ -113,6 +173,13 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const return &dex_file_->GetMethodId(idx); } +const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) { + if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) { + return nullptr; + } + return &dex_file_->GetProtoId(idx); +} + // Helper macro to load string and return false on error. #define LOAD_STRING(var, idx, error) \ const char* (var) = CheckLoadStringByIdx(idx, error); \ @@ -606,12 +673,24 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, return false; } - // Check method access flags. - bool has_code = (code_offset != 0); std::string error_msg; + const char* method_name; + if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) { + ErrorStringPrintf("%s", error_msg.c_str()); + return false; + } + + uint32_t constructor_flags_by_name = 0; + if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) { + ErrorStringPrintf("Bad method name: %s", method_name); + return false; + } + + bool has_code = (code_offset != 0); if (!CheckMethodAccessFlags(idx, access_flags, class_access_flags, + constructor_flags_by_name, has_code, expect_direct, &error_msg)) { @@ -619,6 +698,13 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, return false; } + if (constructor_flags_by_name != 0) { + if (!CheckConstructorProperties(idx, constructor_flags_by_name)) { + DCHECK(FailureReasonIsSet()); + return false; + } + } + return true; } @@ -2653,46 +2739,10 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, return true; } -// Try to find the name of the method with the given index. We do not want to rely on DexFile -// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and -// header_ of the DexFileVerifier. str will contain the pointer to the method name on success -// (flagged by the return value), otherwise error_msg will contain an error string. -static bool FindMethodName(uint32_t method_index, - const uint8_t* begin, - const DexFile::Header* header, - const char** str, - std::string* error_msg) { - if (method_index >= header->method_ids_size_) { - *error_msg = "Method index not available for method flags verification"; - return false; - } - uint32_t string_idx = - (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + - method_index)->name_idx_.index_; - if (string_idx >= header->string_ids_size_) { - *error_msg = "String index not available for method flags verification"; - return false; - } - uint32_t string_off = - (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)-> - string_data_off_; - if (string_off >= header->file_size_) { - *error_msg = "String offset out of bounds for method flags verification"; - return false; - } - const uint8_t* str_data_ptr = begin + string_off; - uint32_t dummy; - if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) { - *error_msg = "String size out of bounds for method flags verification"; - return false; - } - *str = reinterpret_cast<const char*>(str_data_ptr); - return true; -} - bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, uint32_t method_access_flags, uint32_t class_access_flags, + uint32_t constructor_flags_by_name, bool has_code, bool expect_direct, std::string* error_msg) { @@ -2728,36 +2778,23 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, return false; } - // Try to find the name, to check for constructor properties. - const char* str; - if (!FindMethodName(method_index, begin_, header_, &str, error_msg)) { - return false; - } - bool is_init_by_name = false; - constexpr const char* kInitName = "<init>"; - size_t str_offset = (reinterpret_cast<const uint8_t*>(str) - begin_); - if (header_->file_size_ - str_offset >= sizeof(kInitName)) { - is_init_by_name = strcmp(kInitName, str) == 0; - } - bool is_clinit_by_name = false; - constexpr const char* kClinitName = "<clinit>"; - if (header_->file_size_ - str_offset >= sizeof(kClinitName)) { - is_clinit_by_name = strcmp(kClinitName, str) == 0; - } - bool is_constructor = is_init_by_name || is_clinit_by_name; + constexpr uint32_t kConstructorFlags = kAccStatic | kAccConstructor; + const bool is_constructor_by_name = (constructor_flags_by_name & kConstructorFlags) != 0; + const bool is_clinit_by_name = constructor_flags_by_name == kConstructorFlags; // Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce // the reverse for backwards compatibility reasons. - if (((method_access_flags & kAccConstructor) != 0) && !is_constructor) { + if (((method_access_flags & kAccConstructor) != 0) && !is_constructor_by_name) { *error_msg = StringPrintf("Method %" PRIu32 "(%s) is marked constructor, but doesn't match name", - method_index, - GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); + method_index, + GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); return false; } - // Check that the static constructor (= static initializer) is named "<clinit>" and that the - // instance constructor is called "<init>". - if (is_constructor) { + + if (is_constructor_by_name) { + // Check that the static constructor (= static initializer) is named "<clinit>" and that the + // instance constructor is called "<init>". bool is_static = (method_access_flags & kAccStatic) != 0; if (is_static ^ is_clinit_by_name) { *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.", @@ -2772,9 +2809,11 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, } } } + // Check that static and private methods, as well as constructors, are in the direct methods list, // and other methods in the virtual methods list. - bool is_direct = (method_access_flags & (kAccStatic | kAccPrivate)) != 0 || is_constructor; + bool is_direct = ((method_access_flags & (kAccStatic | kAccPrivate)) != 0) || + is_constructor_by_name; if (is_direct != expect_direct) { *error_msg = StringPrintf("Direct/virtual method %" PRIu32 "(%s) not in expected list %d", method_index, @@ -2783,7 +2822,6 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, return false; } - // From here on out it is easier to mask out the bits we're supposed to ignore. method_access_flags &= kMethodAccessFlags; @@ -2819,7 +2857,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, return false; } // Constructors must always have code. - if (is_constructor) { + if (is_constructor_by_name) { *error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); @@ -2881,7 +2919,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, } // Instance constructors must not be synchronized and a few other flags. - if (is_init_by_name) { + if (constructor_flags_by_name == kAccConstructor) { static constexpr uint32_t kInitAllowed = kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic; if ((method_access_flags & ~kInitAllowed) != 0) { @@ -2896,4 +2934,44 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, return true; } +bool DexFileVerifier::CheckConstructorProperties( + uint32_t method_index, + uint32_t constructor_flags) { + DCHECK(constructor_flags == kAccConstructor || + constructor_flags == (kAccConstructor | kAccStatic)); + + // Check signature matches expectations. + const DexFile::MethodId* const method_id = CheckLoadMethodId(method_index, + "Bad <init>/<clinit> method id"); + if (method_id == nullptr) { + return false; + } + + // Check the ProtoId for the corresponding method. + // + // TODO(oth): the error message here is to satisfy the MethodId test + // in the DexFileVerifierTest. The test is checking that the error + // contains this string if the index is out of range. + const DexFile::ProtoId* const proto_id = CheckLoadProtoId(method_id->proto_idx_, + "inter_method_id_item proto_idx"); + if (proto_id == nullptr) { + return false; + } + + Signature signature = dex_file_->GetMethodSignature(*method_id); + if (constructor_flags == (kAccStatic | kAccConstructor)) { + if (!signature.IsVoid() || signature.GetNumberOfParameters() != 0) { + ErrorStringPrintf("<clinit> must have descriptor ()V"); + return false; + } + } else if (!signature.IsVoid()) { + ErrorStringPrintf("Constructor %u(%s) must be void", + method_index, + GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); + return false; + } + + return true; +} + } // namespace art diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 0327367059..ae206132dd 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -153,13 +153,15 @@ class DexFileVerifier { const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt); const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt); - // Load a field/method Id by index. Checks whether the index is in bounds, printing the error if - // not. If there is an error, null is returned. + // Load a field/method/proto Id by index. Checks whether the index is in bounds, printing the + // error if not. If there is an error, null is returned. const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt); const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt); + const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt); void ErrorStringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; + bool FailureReasonIsSet() const { return failure_reason_.size() != 0; } // Retrieve class index and class access flag from the given member. index is the member index, // which is taken as either a field or a method index (as designated by is_field). The result, @@ -177,15 +179,20 @@ class DexFileVerifier { bool CheckFieldAccessFlags(uint32_t idx, uint32_t field_access_flags, uint32_t class_access_flags, - std::string* error_msg); + std::string* error_message); + // Check validity of the given method and access flags, in the context of a class with the given // second access flags. bool CheckMethodAccessFlags(uint32_t method_index, uint32_t method_access_flags, uint32_t class_access_flags, + uint32_t constructor_flags_by_name, bool has_code, bool expect_direct, - std::string* error_msg); + std::string* error_message); + + // Check validity of given method if it's a constructor or class initializer. + bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags); const DexFile* const dex_file_; const uint8_t* const begin_; diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index f14b1d56cb..c56b20057d 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -632,12 +632,8 @@ TEST_F(DexFileVerifierTest, B28552165) { "b28552165", [](DexFile* dex_file) { OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected); - uint32_t method_idx; - FindMethodData(dex_file, "foo", &method_idx); - auto* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(method_idx)); - method_id->name_idx_ = dex::StringIndex(dex_file->NumStringIds()); }, - "Method may have only one of public/protected/private, LMethodFlags;.(error)"); + "Method may have only one of public/protected/private, LMethodFlags;.foo"); } // Set of dex files for interface method tests. As it's not as easy to mutate method names, it's @@ -1674,4 +1670,219 @@ TEST_F(DexFileVerifierTest, Checksum) { EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg; } +TEST_F(DexFileVerifierTest, BadStaticMethodName) { + // Generated DEX file version (037) from: + // + // .class public LBadName; + // .super Ljava/lang/Object; + // + // .method public static <bad_name> (II)V + // .registers 2 + // .prologue + // return-void + // .end method + // + // .method public constructor <init>()V + // .registers 1 + // .prologue + // .line 1 + // invoke-direct {p0}, Ljava/lang/Object;-><init>()V + // return-void + // .end method + // + static const char kDexBase64[] = + "ZGV4CjAzNwC2NYlwyxEc/h6hv+hMeUVQPtiX6MQBcfgwAgAAcAAAAHhWNBIAAAAAAAAAAJABAAAI" + "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABAAQAA8AAAAPAA" + "AAD8AAAABAEAABIBAAAVAQAAIAEAADQBAAA3AQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA" + "AAcAAAADAAAAPAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA" + "AAAAAIABAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgAMQmFkTmFtZS5qYXZhAAFJAAlMQmFkTmFt" + "ZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgADVklJAAIAAAAAAAAAAAAAAAACAAAHAAEABw4AAAIA" + "AgAAAAAASAEAAAEAAAAOAAAAAQABAAEAAABOAQAABAAAAHAQAgAAAA4AAAACAAAJ1AIBgYAE6AIA" + "AA0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAAAgAAAKAAAAAFAAAA" + "AwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAADwBAAADEAAAAQAAAEQBAAAD" + "IAAAAgAAAEgBAAABIAAAAgAAAFQBAAAAIAAAAQAAAIABAAAAEAAAAQAAAJABAAA="; + + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad static method name", + /*verify_checksum*/ true, + &error_msg)); +} + +TEST_F(DexFileVerifierTest, BadVirtualMethodName) { + // Generated DEX file version (037) from: + // + // .class public LBadVirtualName; + // .super Ljava/lang/Object; + // + // .method public <bad_name> (II)V + // .registers 2 + // return-void + // .end method + // + // .method public constructor <init>()V + // .registers 1 + // invoke-direct {p0}, Ljava/lang/Object;-><init>()V + // return-void + // .end method + // + static const char kDexBase64[] = + "ZGV4CjAzNwDcPC8B2E7kYTZmeHX2u2IqrpWV9EXBHpE8AgAAcAAAAHhWNBIAAAAAAAAAAJwBAAAI" + "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABMAQAA8AAAAPAA" + "AAD8AAAABAEAABkBAAAcAQAALgEAAEIBAABFAQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA" + "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA" + "AAAAAI4BAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgATQmFkVmlydHVhbE5hbWUuamF2YQABSQAQ" + "TEJhZFZpcnR1YWxOYW1lOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAANWSUkAAAACAAAAAAAAAAAA" + "AAABAAcOAAACAAAHAAABAAEAAQAAAFgBAAAEAAAAcBACAAAADgADAAMAAAAAAF0BAAABAAAADgAA" + "AAEBAYGABOQCAAH8Ag0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAA" + "AgAAAKAAAAAFAAAAAwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAAEwBAAAD" + "EAAAAQAAAFQBAAADIAAAAgAAAFgBAAABIAAAAgAAAGQBAAAAIAAAAQAAAI4BAAAAEAAAAQAAAJwB" + "AAA="; + + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad virtual method name", + /*verify_checksum*/ true, + &error_msg)); +} + +TEST_F(DexFileVerifierTest, BadClinitSignature) { + // Generated DEX file version (037) from: + // + // .class public LOneClinitBadSig; + // .super Ljava/lang/Object; + // + // .method public static constructor <clinit>(II)V + // .registers 2 + // return-void + // .end method + // + // .method public constructor <init>()V + // .registers 1 + // invoke-direct {p0}, Ljava/lang/Object;-><init>()V + // return-void + // .end method + // + static const char kDexBase64[] = + "ZGV4CjAzNwBNOwTbfJmWq5eMOlxUY4EICGiEGJMVg8RAAgAAcAAAAHhWNBIAAAAAAAAAAKABAAAI" + "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABQAQAA8AAAAPAA" + "AAD6AAAAAgEAAAUBAAAYAQAALAEAAEIBAABFAQAAAgAAAAMAAAAEAAAABgAAAAYAAAADAAAAAAAA" + "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAAFAAAA" + "AAAAAJABAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AAUkAEUxPbmVDbGluaXRCYWRTaWc7ABJMamF2" + "YS9sYW5nL09iamVjdDsAFE9uZUNsaW5pdEJhZFNpZy5qYXZhAAFWAANWSUkAAAACAAAAAAAAAAAA" + "AAAAAgAABwABAAcOAAACAAIAAAAAAFgBAAABAAAADgAAAAEAAQABAAAAXgEAAAQAAABwEAIAAAAO" + "AAAAAgAAiYAE5AIBgYAE+AINAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAA" + "AwAAAAIAAACgAAAABQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABM" + "AQAAAxAAAAEAAABUAQAAAyAAAAIAAABYAQAAASAAAAIAAABkAQAAACAAAAEAAACQAQAAABAAAAEA" + "AACgAQAA"; + + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad clinit signature", + /*verify_checksum*/ true, + &error_msg)); +} + +TEST_F(DexFileVerifierTest, BadClinitSignatureAgain) { + // Generated DEX file version (037) from: + // + // .class public LOneClinitBadSigAgain; + // .super Ljava/lang/Object; + // + // .method public static constructor <clinit>()I + // .registers 1 + // const/4 v0, 1 + // return v0 + // .end method + // + // .method public constructor <init>()V + // .registers 1 + // invoke-direct {p0}, Ljava/lang/Object;-><init>()V + // return-void + // .end method + // + static const char kDexBase64[] = + "ZGV4CjAzNwBfPcPu5NVwKUqZIu/YR8xqVlVD5UzTk0gEAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH" + "AAAAcAAAAAQAAACMAAAAAgAAAJwAAAAAAAAAAAAAAAMAAAC0AAAAAQAAAMwAAAAYAQAA7AAAAOwA" + "AAD2AAAA/gAAAAEBAAAZAQAALQEAAEgBAAACAAAAAwAAAAQAAAAGAAAAAgAAAAAAAAAAAAAABgAA" + "AAMAAAAAAAAAAQAAAAAAAAABAAEAAQAAAAIAAQABAAAAAQAAAAEAAAACAAAAAAAAAAUAAAAAAAAA" + "eAEAAAAAAAAIPGNsaW5pdD4ABjxpbml0PgABSQAWTE9uZUNsaW5pdEJhZFNpZ0FnYWluOwASTGph" + "dmEvbGFuZy9PYmplY3Q7ABlPbmVDbGluaXRCYWRTaWdBZ2Fpbi5qYXZhAAFWAAABAAAAAAAAAAAA" + "AAACAAAAEhAPAAEAAQABAAAAAAAAAAQAAABwEAIAAAAOAAAAAgAAiYAEzAIBgYAE4AIKAAAAAAAA" + "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAIAAACcAAAABQAAAAMAAAC0AAAA" + "BgAAAAEAAADMAAAAAiAAAAcAAADsAAAAASAAAAIAAABMAQAAACAAAAEAAAB4AQAAABAAAAEAAACI" + "AQAA"; + + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad clinit signature", + /*verify_checksum*/ true, + &error_msg)); +} + +TEST_F(DexFileVerifierTest, BadInitSignature) { + // Generated DEX file version (037) from: + // + // .class public LBadInitSig; + // .super Ljava/lang/Object; + // + // .method public constructor <init>()I + // .registers 1 + // invoke-direct {p0}, Ljava/lang/Object;-><init>()V + // const v0, 1 + // return v0 + // .end method + // + static const char kDexBase64[] = + "ZGV4CjAzNwCdMdeh1KoHWamF2Prq32LF39YZ78fV7q+wAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF" + "AAAAcAAAAAQAAACEAAAAAgAAAJQAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAADUAAAA3AAAANwA" + "AADkAAAA5wAAAPUAAAAJAQAAAQAAAAIAAAADAAAABAAAAAEAAAAAAAAAAAAAAAQAAAADAAAAAAAA" + "AAEAAAAAAAAAAgABAAAAAAABAAAAAQAAAAIAAAAAAAAA/////wAAAAAqAQAAAAAAAAY8aW5pdD4A" + "AUkADExCYWRJbml0U2lnOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEAAQABAAAAAAAAAAcAAABw" + "EAEAAAAUAAEAAAAPAAAAAQAAgYAEjAIKAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAQA" + "AACEAAAAAwAAAAIAAACUAAAABQAAAAIAAACsAAAABgAAAAEAAAC8AAAAAiAAAAUAAADcAAAAASAA" + "AAEAAAAMAQAAACAAAAEAAAAqAQAAABAAAAEAAAA0AQAA"; + + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length)); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad init signature", + /*verify_checksum*/ true, + &error_msg)); +} + } // namespace art diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 7b8974fa5a..37f3ac92e9 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -358,7 +358,7 @@ std::string Instruction::DumpString(const DexFile* file) const { } break; case k35c: { - uint32_t arg[5]; + uint32_t arg[kMaxVarArgRegs]; GetVarArgs(arg); switch (Opcode()) { case FILLED_NEW_ARRAY: @@ -443,8 +443,50 @@ std::string Instruction::DumpString(const DexFile* file) const { } break; } + case k45cc: { + uint32_t arg[kMaxVarArgRegs]; + GetVarArgs(arg); + uint32_t method_idx = VRegB_45cc(); + uint32_t proto_idx = VRegH_45cc(); + os << opcode << " {"; + for (int i = 0; i < VRegA_45cc(); ++i) { + if (i != 0) { + os << ", "; + } + os << "v" << arg[i]; + } + os << "}"; + if (file != nullptr) { + os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << " // "; + } else { + os << ", "; + } + os << "method@" << method_idx << ", proto@" << proto_idx; + break; + } + case k4rcc: + switch (Opcode()) { + case INVOKE_POLYMORPHIC_RANGE: { + if (file != nullptr) { + uint32_t method_idx = VRegB_4rcc(); + uint32_t proto_idx = VRegH_4rcc(); + os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) + << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << " // method@" << method_idx << ", proto@" << proto_idx; + break; + } + } + FALLTHROUGH_INTENDED; + default: { + uint32_t method_idx = VRegB_4rcc(); + uint32_t proto_idx = VRegH_4rcc(); + os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) + << "}, method@" << method_idx << ", proto@" << proto_idx; + } + } + break; case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break; - default: os << " unknown format (" << DumpHex(5) << ")"; break; } return os.str(); } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 469c45c10c..ac0ce36016 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -52,21 +52,19 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, // suspended while executing it. ScopedAssertNoThreadSuspension sants(__FUNCTION__); + if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) { + return inline_info.GetArtMethodAtDepth(encoding, inlining_depth); + } + uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, inlining_depth); - InvokeType invoke_type = static_cast<InvokeType>( - inline_info.GetInvokeTypeAtDepth(encoding, inlining_depth)); - ArtMethod* inlined_method = outer_method->GetDexCacheResolvedMethod(method_index, - kRuntimePointerSize); - if (!inlined_method->IsRuntimeMethod()) { + if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) { + // "charAt" special case. It is the only non-leaf method we inline across dex files. + ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); + DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index); return inlined_method; } - // The method in the dex cache is the runtime method responsible for invoking - // the stub that will then update the dex cache. Therefore, we need to do the - // resolution ourselves. - - // We first find the dex cache of our caller. If it is the outer method, we can directly - // use its dex cache. Otherwise, we also need to resolve our caller. + // Find which method did the call in the inlining hierarchy. ArtMethod* caller = outer_method; if (inlining_depth != 0) { caller = GetResolvedMethod(outer_method, @@ -74,59 +72,41 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, encoding, inlining_depth - 1); } - DCHECK_EQ(caller->GetDexCache(), outer_method->GetDexCache()) - << "Compiler only supports inlining calls within the same dex cache"; - const DexFile* dex_file = outer_method->GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); - if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) { - // "charAt" special case. It is the only non-leaf method we inline across dex files. - if (kIsDebugBuild) { - const char* name = dex_file->StringDataByIdx(method_id.name_idx_); - DCHECK_EQ(std::string(name), "charAt"); - DCHECK_EQ(std::string(dex_file->GetMethodShorty(method_id)), "CI") - << std::string(dex_file->GetMethodShorty(method_id)); - DCHECK_EQ(std::string(dex_file->StringByTypeIdx(method_id.class_idx_)), "Ljava/lang/String;") - << std::string(dex_file->StringByTypeIdx(method_id.class_idx_)); - } - mirror::Class* cls = - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangString); - // Update the dex cache for future lookups. - caller->GetDexCache()->SetResolvedType(method_id.class_idx_, cls); - inlined_method = cls->FindVirtualMethod("charAt", "(I)C", kRuntimePointerSize); - } else { - mirror::Class* klass = caller->GetDexCache()->GetResolvedType(method_id.class_idx_); - DCHECK_EQ(klass->GetDexCache(), caller->GetDexCache()) - << "Compiler only supports inlining calls within the same dex cache"; - switch (invoke_type) { - case kDirect: - case kStatic: - inlined_method = - klass->FindDirectMethod(klass->GetDexCache(), method_index, kRuntimePointerSize); - break; - case kSuper: - case kVirtual: - inlined_method = - klass->FindVirtualMethod(klass->GetDexCache(), method_index, kRuntimePointerSize); - break; - default: - LOG(FATAL) << "Unimplemented inlined invocation type: " << invoke_type; - UNREACHABLE(); + // Lookup the declaring class of the inlined method. + const DexFile* dex_file = caller->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); + const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader(); + mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader); + if (klass == nullptr) { + LOG(FATAL) << "Could not find an inlined method from an .oat file: " + << "the class " << descriptor << " was not found in the class loader of " + << caller->PrettyMethod() << ". " + << "This must be due to playing wrongly with class loaders"; + } + + // Lookup the method. + const char* method_name = dex_file->GetMethodName(method_id); + const Signature signature = dex_file->GetMethodSignature(method_id); + + ArtMethod* inlined_method = + klass->FindDeclaredDirectMethod(method_name, signature, kRuntimePointerSize); + if (inlined_method == nullptr) { + inlined_method = klass->FindDeclaredVirtualMethod(method_name, signature, kRuntimePointerSize); + if (inlined_method == nullptr) { + LOG(FATAL) << "Could not find an inlined method from an .oat file: " + << "the class " << descriptor << " does not have " + << method_name << signature << " declared. " + << "This must be due to duplicate classes or playing wrongly with class loaders"; } } - // Update the dex cache for future lookups. Note that for static methods, this is safe - // when the class is being initialized, as the entrypoint for the ArtMethod is at - // this point still the resolution trampoline. - outer_method->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize); return inlined_method; } -inline ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type) { - return GetCalleeSaveMethodCaller( - self->GetManagedStack()->GetTopQuickFrame(), type, true /* do_caller_check */); -} - ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(mirror::Class* klass, Thread* self, bool* slow_path) @@ -261,10 +241,9 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, *slow_path = true; return nullptr; // Failure } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = class_linker->GetImagePointerSize(); - mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size); + mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); klass = class_linker->ResolveType(type_idx, method); *slow_path = true; if (klass == nullptr) { // Error @@ -314,11 +293,10 @@ inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, klass->GetComponentSizeShift(), allocator_type); } -template <bool kAccessCheck, bool kInstrumented> +template <bool kInstrumented> ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, int32_t component_count, - ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) { DCHECK(klass != nullptr); @@ -326,13 +304,6 @@ inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, ThrowNegativeArraySizeException(component_count); return nullptr; // Failure } - if (kAccessCheck) { - mirror::Class* referrer = method->GetDeclaringClass(); - if (UNLIKELY(!referrer->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer, klass); - return nullptr; // Failure - } - } // No need to retry a slow-path allocation as the above code won't cause a GC or thread // suspension. return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 5390165ecd..25fd727968 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -38,98 +38,13 @@ namespace art { -static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check) - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { - if (UNLIKELY(component_count < 0)) { - ThrowNegativeArraySizeException(component_count); - return nullptr; // Failure - } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = class_linker->GetImagePointerSize(); - mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size); - if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve - klass = class_linker->ResolveType(type_idx, referrer); - if (klass == nullptr) { // Error - DCHECK(self->IsExceptionPending()); - return nullptr; // Failure - } - } - if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) { - if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) { - ThrowRuntimeException("Bad filled array request for type %s", - klass->PrettyDescriptor().c_str()); - } else { - self->ThrowNewExceptionF( - "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but 'int'", - klass->PrettyDescriptor().c_str()); - } - return nullptr; // Failure - } - if (access_check) { - mirror::Class* referrer_klass = referrer->GetDeclaringClass(); - if (UNLIKELY(!referrer_klass->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer_klass, klass); - return nullptr; // Failure - } - } - DCHECK(klass->IsArrayClass()) << klass->PrettyClass(); - return klass; -} - -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { - mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, - access_check); - if (UNLIKELY(klass == nullptr)) { - return nullptr; - } - // Always go slow path for now, filled new array is not common. - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then - // the heap switched the allocator type while we were suspended. - return mirror::Array::Alloc<false>(self, klass, component_count, - klass->GetComponentSizeShift(), - heap->GetCurrentAllocator()); -} - -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCodeInstrumented( - dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { - mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, - access_check); - if (UNLIKELY(klass == nullptr)) { - return nullptr; - } - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then - // the heap switched the allocator type while we were suspended. - return mirror::Array::Alloc<true>(self, klass, component_count, - klass->GetComponentSizeShift(), - heap->GetCurrentAllocator()); -} - void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) { if (o.Get() == nullptr) { return; } // Make sure that the result is an instance of the type this method was expected to return. ArtMethod* method = self->GetCurrentMethod(nullptr); - mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize); + mirror::Class* return_type = method->GetReturnType(true /* resolve */); if (!o->InstanceOf(return_type)) { Runtime::Current()->GetJavaVM()->JniAbortF(nullptr, @@ -192,8 +107,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons ArtMethod* interface_method = soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod(); // This can cause thread suspension. - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size); + mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */); ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result); JValue result_unboxed; if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) { @@ -261,11 +175,8 @@ bool FillArrayData(ObjPtr<mirror::Object> obj, const Instruction::ArrayDataPaylo return true; } -ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, - Runtime::CalleeSaveType type, - bool do_caller_check) - REQUIRES_SHARED(Locks::mutator_lock_) { - ScopedAssertNoThreadSuspension ants(__FUNCTION__); +static inline std::pair<ArtMethod*, uintptr_t> DoGetCalleeSaveMethodOuterCallerAndPc( + ArtMethod** sp, Runtime::CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type)); const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type); @@ -275,6 +186,13 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>( (reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset)); ArtMethod* outer_method = *caller_sp; + return std::make_pair(outer_method, caller_pc); +} + +static inline ArtMethod* DoGetCalleeSaveMethodCaller(ArtMethod* outer_method, + uintptr_t caller_pc, + bool do_caller_check) + REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* caller = outer_method; if (LIKELY(caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()))) { if (outer_method != nullptr) { @@ -308,8 +226,33 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, visitor.WalkStack(); caller = visitor.caller; } + return caller; +} +ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, + Runtime::CalleeSaveType type, + bool do_caller_check) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); + ArtMethod* outer_method = outer_caller_and_pc.first; + uintptr_t caller_pc = outer_caller_and_pc.second; + ArtMethod* caller = DoGetCalleeSaveMethodCaller(outer_method, caller_pc, do_caller_check); return caller; } +CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, + Runtime::CalleeSaveType type) { + CallerAndOuterMethod result; + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); + result.outer_method = outer_caller_and_pc.first; + uintptr_t caller_pc = outer_caller_and_pc.second; + result.caller = + DoGetCalleeSaveMethodCaller(result.outer_method, caller_pc, /* do_caller_check */ true); + return result; +} + + } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 4794610ca8..6a04f20f3a 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -93,33 +93,14 @@ ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -template <bool kAccessCheck, bool kInstrumented> +template <bool kInstrumented> ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, int32_t component_count, - ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - -mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - // Type of find field operation for fast and slow case. enum FindFieldType { InstanceObjectRead, @@ -201,7 +182,13 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, bool do_caller_check = false) REQUIRES_SHARED(Locks::mutator_lock_); -ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type) +struct CallerAndOuterMethod { + ArtMethod* caller; + ArtMethod* outer_method; +}; + +CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, + Runtime::CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_); } // namespace art diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 2d06508069..e9f09b2bd9 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -82,72 +82,12 @@ extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \ REQUIRES_SHARED(Locks::mutator_lock_) { \ return artAllocObjectFromCode<true, false, instrumented_bool, allocator_type>(klass, self); \ } \ -extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - return AllocArrayFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - allocator_type); \ -} \ extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \ - mirror::Class* klass, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, component_count, method, self, \ - allocator_type); \ -} \ -extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - return AllocArrayFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - allocator_type); \ -} \ -extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - false, \ - allocator_type); \ - } else { \ - return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - false, \ - allocator_type); \ - } \ -} \ -extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ + mirror::Class* klass, int32_t component_count, Thread* self) \ REQUIRES_SHARED(Locks::mutator_lock_) { \ ScopedQuickEntrypointChecks sqec(self); \ - if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - true, \ - allocator_type); \ - } else { \ - return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - true, \ - allocator_type); \ - } \ + return AllocArrayFromCodeResolved<instrumented_bool>(klass, component_count, self, \ + allocator_type); \ } \ extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \ mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \ @@ -188,9 +128,7 @@ GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(Region, gc::kAllocatorTypeRegion) GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RegionTLAB, gc::kAllocatorTypeRegionTLAB) #define GENERATE_ENTRYPOINTS(suffix) \ -extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \ -extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, ArtMethod* ref); \ -extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t); \ extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass); \ extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass); \ extern "C" void* art_quick_alloc_object_with_checks##suffix(mirror::Class* klass); \ @@ -200,7 +138,7 @@ extern "C" void* art_quick_alloc_string_from_bytes##suffix(void*, int32_t, int32 extern "C" void* art_quick_alloc_string_from_chars##suffix(int32_t, int32_t, void*); \ extern "C" void* art_quick_alloc_string_from_string##suffix(void*); \ extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \ -extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, ArtMethod* ref); \ +extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t); \ extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \ extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \ extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass); \ @@ -213,26 +151,18 @@ extern "C" void* art_quick_alloc_string_from_chars##suffix##_instrumented(int32_ extern "C" void* art_quick_alloc_string_from_string##suffix##_instrumented(void*); \ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrumented) { \ if (instrumented) { \ - qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \ qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix##_instrumented; \ - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix##_instrumented; \ qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \ - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \ - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \ } else { \ - qpoints->pAllocArray = art_quick_alloc_array##suffix; \ qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix; \ - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix; \ qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \ - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \ - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \ diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 64030f36bc..2d0932a0c4 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -109,8 +109,13 @@ extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, v extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*); + extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*); +// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or +// reference return type. +extern "C" void art_quick_invoke_polymorphic(uint32_t, void*); + // Thread entrypoints. extern "C" void art_quick_test_suspend(); diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 78dad94dfe..6481b97ae1 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -66,10 +66,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) qpoints->pGetObjStatic = art_quick_get_obj_static; // Array - qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; - qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; qpoints->pAputObject = art_quick_aput_obj; - qpoints->pHandleFillArrayData = art_quick_handle_fill_data; // JNI qpoints->pJniMethodStart = JniMethodStart; @@ -106,6 +103,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) art_quick_invoke_super_trampoline_with_access_check; qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check; + qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 5dad43e7fa..5b1b2871c2 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -31,22 +31,56 @@ namespace art { +static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) { + // For AOT code, we need a write barrier for the class loader that holds + // the GC roots in the .bss. + const DexFile* dex_file = outer_method->GetDexFile(); + if (dex_file != nullptr && + dex_file->GetOatDexFile() != nullptr && + !dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) { + mirror::ClassLoader* class_loader = outer_method->GetClassLoader(); + if (class_loader != nullptr) { + DCHECK(!class_loader->GetClassTable()->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile())) + << "Oat file with .bss GC roots was not registered in class table: " + << dex_file->GetOatDexFile()->GetOatFile()->GetLocation(); + // Note that we emit the barrier before the compiled code stores the String or Class + // as a GC root. This is OK as there is no suspend point point in between. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + } else { + Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots( + dex_file->GetOatDexFile()->GetOatFile()); + } + } +} + extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Called to ensure static storage base is initialized for direct static field reads and writes. // A class may be accessing another class' fields when it doesn't have access, as access has been // given by inheritance. ScopedQuickEntrypointChecks sqec(self); - auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly); - return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly); + ArtMethod* caller = caller_and_outer.caller; + mirror::Class* result = + ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); + if (LIKELY(result != nullptr)) { + BssWriteBarrier(caller_and_outer.outer_method); + } + return result; } extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Called when method->dex_cache_resolved_types_[] misses. ScopedQuickEntrypointChecks sqec(self); - auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly); - return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly); + ArtMethod* caller = caller_and_outer.caller; + mirror::Class* result = + ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); + if (LIKELY(result != nullptr)) { + BssWriteBarrier(caller_and_outer.outer_method); + } + return result; } extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self) @@ -54,36 +88,28 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type // Called when caller isn't guaranteed to have access to a type and the dex cache may be // unpopulated. ScopedQuickEntrypointChecks sqec(self); - auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly); - return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly); + ArtMethod* caller = caller_and_outer.caller; + mirror::Class* result = + ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); + if (LIKELY(result != nullptr)) { + BssWriteBarrier(caller_and_outer.outer_method); + } + return result; } extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); - auto* caller = GetCalleeSaveMethodCaller( + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, // TODO: Change art_quick_resolve_string on MIPS and MIPS64 to kSaveEverything. (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly : Runtime::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); if (LIKELY(result != nullptr)) { - // For AOT code, we need a write barrier for the class loader that holds - // the GC roots in the .bss. - const DexFile* dex_file = caller->GetDexFile(); - if (dex_file != nullptr && - dex_file->GetOatDexFile() != nullptr && - !dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) { - mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader(); - DCHECK(class_loader != nullptr); // We do not use .bss GC roots for boot image. - DCHECK( - !class_loader->GetClassTable()->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile())) - << "Oat file with .bss GC roots was not registered in class table: " - << dex_file->GetOatDexFile()->GetOatFile()->GetLocation(); - // Note that we emit the barrier before the compiled code stores the string as GC root. - // This is OK as there is no suspend point point in between. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); - } + BssWriteBarrier(caller_and_outer.outer_method); } return result; } diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 0911aeb0f4..22b0f921c7 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -20,14 +20,10 @@ // All quick entrypoints. Format is name, return type, argument types. #define QUICK_ENTRYPOINT_LIST(V) \ - V(AllocArray, void*, uint32_t, int32_t, ArtMethod*) \ - V(AllocArrayResolved, void*, mirror::Class*, int32_t, ArtMethod*) \ - V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \ + V(AllocArrayResolved, void*, mirror::Class*, int32_t) \ V(AllocObjectResolved, void*, mirror::Class*) \ V(AllocObjectInitialized, void*, mirror::Class*) \ V(AllocObjectWithChecks, void*, mirror::Class*) \ - V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \ - V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \ V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \ V(AllocStringFromChars, void*, int32_t, int32_t, void*) \ V(AllocStringFromString, void*, void*) \ @@ -65,10 +61,7 @@ V(GetObjInstance, void*, uint32_t, void*) \ V(GetObjStatic, void*, uint32_t) \ \ - V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \ - V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \ V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \ - V(HandleFillArrayData, void, void*, void*) \ \ V(JniMethodStart, uint32_t, Thread*) \ V(JniMethodFastStart, uint32_t, Thread*) \ @@ -133,6 +126,7 @@ V(InvokeStaticTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \ + V(InvokePolymorphic, void, uint32_t, void*) \ \ V(TestSuspend, void, void) \ \ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index a3e5b552b5..eb76fb6b88 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -27,10 +27,12 @@ #include "imtable-inl.h" #include "interpreter/interpreter.h" #include "linear_alloc.h" +#include "method_handles.h" #include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/method.h" +#include "mirror/method_handle_impl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "oat_quick_method_header.h" @@ -39,6 +41,7 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "debugger.h" +#include "well_known_classes.h" namespace art { @@ -2391,4 +2394,121 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUT reinterpret_cast<uintptr_t>(method)); } +// Returns shorty type so the caller can determine how to put |result| +// into expected registers. The shorty type is static so the compiler +// could call different flavors of this code path depending on the +// shorty type though this would require different entry points for +// each type. +extern "C" uintptr_t artInvokePolymorphic( + JValue* result, + mirror::Object* raw_method_handle, + Thread* self, + ArtMethod** sp) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveRefsAndArgs)); + + // Start new JNI local reference state + JNIEnvExt* env = self->GetJniEnv(); + ScopedObjectAccessUnchecked soa(env); + ScopedJniEnvLocalRefState env_state(env); + const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe."); + + // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC. + ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); + uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); + const DexFile::CodeItem* code = caller_method->GetCodeItem(); + const Instruction* inst = Instruction::At(&code->insns_[dex_pc]); + DCHECK(inst->Opcode() == Instruction::INVOKE_POLYMORPHIC || + inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); + const DexFile* dex_file = caller_method->GetDexFile(); + const uint32_t proto_idx = inst->VRegH(); + const char* shorty = dex_file->GetShorty(proto_idx); + const size_t shorty_length = strlen(shorty); + static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. + RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa); + gc_visitor.VisitArguments(); + + // Wrap raw_method_handle in a Handle for safety. + StackHandleScope<5> hs(self); + Handle<mirror::MethodHandleImpl> method_handle( + hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle)))); + raw_method_handle = nullptr; + self->EndAssertNoThreadSuspension(old_cause); + + // Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact(). + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::kForceICCECheck>(self, + inst->VRegB(), + caller_method, + kVirtual); + DCHECK((resolved_method == + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) || + (resolved_method == + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invoke))); + if (UNLIKELY(method_handle.IsNull())) { + ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual); + return static_cast<uintptr_t>('V'); + } + + Handle<mirror::Class> caller_class(hs.NewHandle(caller_method->GetDeclaringClass())); + Handle<mirror::MethodType> method_type(hs.NewHandle(linker->ResolveMethodType( + *dex_file, proto_idx, + hs.NewHandle<mirror::DexCache>(caller_class->GetDexCache()), + hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader())))); + // This implies we couldn't resolve one or more types in this method handle. + if (UNLIKELY(method_type.IsNull())) { + CHECK(self->IsExceptionPending()); + return static_cast<uintptr_t>('V'); + } + + DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst->VRegA()); + DCHECK_EQ(resolved_method->IsStatic(), kMethodIsStatic); + + // Fix references before constructing the shadow frame. + gc_visitor.FixupReferences(); + + // Construct shadow frame placing arguments consecutively from |first_arg|. + const bool is_range = (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); + const size_t num_vregs = is_range ? inst->VRegA_4rcc() : inst->VRegA_45cc(); + const size_t first_arg = 0; + ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = + CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, resolved_method, dex_pc); + ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get(); + ScopedStackedShadowFramePusher + frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction); + BuildQuickShadowFrameVisitor shadow_frame_builder(sp, + kMethodIsStatic, + shorty, + strlen(shorty), + shadow_frame, + first_arg); + shadow_frame_builder.VisitArguments(); + + // Push a transition back into managed code onto the linked list in thread. + ManagedStack fragment; + self->PushManagedStackFragment(&fragment); + + // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in + // consecutive order. + uint32_t unused_args[Instruction::kMaxVarArgRegs] = {}; + uint32_t first_callee_arg = first_arg + 1; + const bool do_assignability_check = false; + if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self, + resolved_method, + *shadow_frame, + method_handle, + method_type, + unused_args, + first_callee_arg, + result)) { + DCHECK(self->IsExceptionPending()); + } + + // Pop transition record. + self->PopManagedStackFragment(fragment); + + return static_cast<uintptr_t>(shorty[0]); +} + } // namespace art diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 6866abb6ae..8e84d76e83 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -117,15 +117,14 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, active_suspend_barriers, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, jni_entrypoints, + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, thread_local_start, sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers)); - - // Skip across the entrypoints structures. - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(size_t)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, jni_entrypoints, sizeof(size_t)); + + // Skip across the entrypoints structures. EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*)); @@ -151,23 +150,16 @@ class EntrypointsOrderTest : public CommonRuntimeTest { } void CheckQuickEntryPoints() { - CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pAllocArray) == 0, - QuickEntryPoints_start_with_allocarray); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArray, pAllocArrayResolved, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayResolved, pAllocArrayWithAccessCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayWithAccessCheck, pAllocObjectResolved, + CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pAllocArrayResolved) == 0, + QuickEntryPoints_start_with_allocarray_resoved); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocArrayResolved, pAllocObjectResolved, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectResolved, pAllocObjectInitialized, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray, + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck, - pAllocStringFromBytes, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString, @@ -206,13 +198,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck, - pAputObjectWithBoundCheck, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized, @@ -286,6 +273,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeSuperTrampolineWithAccessCheck, pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck, + pInvokePolymorphic, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pTestSuspend, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*)); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 7b86339663..6044053b4f 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -846,7 +846,7 @@ void ConcurrentCopying::IssueEmptyCheckpoint() { // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks. // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well. { - ReaderMutexLock mu0(self, *Locks::mutator_lock_); + ScopedObjectAccess soa(self); MutexLock mu1(self, *Locks::thread_list_lock_); for (Thread* thread : thread_list->GetList()) { uint32_t tid = thread->GetThreadId(); diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index e71a397039..4c6b5bfadd 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -141,16 +141,6 @@ mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, return nullptr; } mirror::Object* const obj = reinterpret_cast<mirror::Object*>(mem_map->Begin()); - if (kIsDebugBuild) { - ReaderMutexLock mu2(Thread::Current(), *Locks::heap_bitmap_lock_); - auto* heap = Runtime::Current()->GetHeap(); - auto* live_bitmap = heap->GetLiveBitmap(); - auto* space_bitmap = live_bitmap->GetContinuousSpaceBitmap(obj); - CHECK(space_bitmap == nullptr) << obj << " overlaps with bitmap " << *space_bitmap; - auto* obj_end = reinterpret_cast<mirror::Object*>(mem_map->End()); - space_bitmap = live_bitmap->GetContinuousSpaceBitmap(obj_end - 1); - CHECK(space_bitmap == nullptr) << obj_end << " overlaps with bitmap " << *space_bitmap; - } MutexLock mu(self, lock_); large_objects_.Put(obj, LargeObject {mem_map, false /* not zygote */}); const size_t allocation_size = mem_map->BaseSize(); diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index bebcd71951..af57397f96 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -60,17 +60,13 @@ DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (s DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k32).Int32Value()))) #define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k64).Int32Value()))) -#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_32 24 -DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_TYPES_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedTypesOffset(art::PointerSize::k32).Int32Value()))) -#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_64 32 -DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedTypesOffset(art::PointerSize::k64).Int32Value()))) -#define ART_METHOD_JNI_OFFSET_32 28 +#define ART_METHOD_JNI_OFFSET_32 24 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k32).Int32Value()))) -#define ART_METHOD_JNI_OFFSET_64 40 +#define ART_METHOD_JNI_OFFSET_64 32 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k64).Int32Value()))) -#define ART_METHOD_QUICK_CODE_OFFSET_32 32 +#define ART_METHOD_QUICK_CODE_OFFSET_32 28 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k32).Int32Value()))) -#define ART_METHOD_QUICK_CODE_OFFSET_64 48 +#define ART_METHOD_QUICK_CODE_OFFSET_64 40 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value()))) #define ART_METHOD_DECLARING_CLASS_OFFSET 0 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value()))) diff --git a/runtime/image.cc b/runtime/image.cc index 2ef60c3e07..6d8889532f 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', '4', '\0' }; // mirror::Class update +const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '5', '\0' }; // ArtMethod update ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ca26207093..28bcb97105 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -596,7 +596,7 @@ bool DoInvokePolymorphic(Thread* self, // Get the register arguments for the invoke. inst->GetVarArgs(args, inst_data); // Drop the first register which is the method handle performing the invoke. - memcpy(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); + memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); args[Instruction::kMaxVarArgRegs - 1] = 0; return DoInvokePolymorphic<is_range, do_access_check>(self, invoke_method, @@ -751,16 +751,14 @@ static inline bool DoCallCommon(ArtMethod* called_method, case 'L': { ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg); if (do_assignability_check && o != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_; - ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx, - pointer_size); + ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx); if (arg_type == nullptr) { StackHandleScope<1> hs(self); // Preserve o since it is used below and GetClassFromTypeIndex may cause thread // suspension. HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o); - arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size); + arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */); if (arg_type == nullptr) { CHECK(self->IsExceptionPending()); return false; diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index d7dfcd4408..a77a3fc2b3 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -285,9 +285,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, const size_t ref_idx = inst->VRegA_11x(inst_data); ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx); if (do_assignability_check && obj_result != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */, - pointer_size); + ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */); // Re-load since it might have moved. obj_result = shadow_frame.GetVRegReference(ref_idx); if (return_type == nullptr) { diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index b7125a8a43..2bb8819cb3 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -26,7 +26,7 @@ #include "jit_code_cache.h" #include "oat_file_manager.h" #include "oat_quick_method_header.h" -#include "offline_profiling_info.h" +#include "profile_compilation_info.h" #include "profile_saver.h" #include "runtime.h" #include "runtime_options.h" diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 05c390590b..4112142a4f 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -25,7 +25,7 @@ #include "jit/profile_saver_options.h" #include "obj_ptr.h" #include "object_callbacks.h" -#include "offline_profiling_info.h" +#include "profile_compilation_info.h" #include "thread_pool.h" namespace art { diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/profile_compilation_info.cc index 6f2a8c673f..1405c40096 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "offline_profiling_info.h" +#include "profile_compilation_info.h" #include "errno.h" #include <limits.h> diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/profile_compilation_info.h index 53d0eea932..f8061bcfd8 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_ -#define ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_ +#ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ +#define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ #include <set> #include <vector> @@ -29,7 +29,6 @@ namespace art { -// TODO: rename file. /** * Profile information in a format suitable to be queried by the compiler and * performing profile guided compilation. @@ -187,4 +186,4 @@ class ProfileCompilationInfo { } // namespace art -#endif // ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_ +#endif // ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_ diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 1dd1e36e74..835a5f3495 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -25,7 +25,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "handle_scope-inl.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 59e2c94790..9c5e41fd13 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -19,7 +19,7 @@ #include "base/mutex.h" #include "jit_code_cache.h" -#include "offline_profiling_info.h" +#include "profile_compilation_info.h" #include "profile_saver_options.h" #include "safe_map.h" diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc index 5a3fafa726..0148a1c3b0 100644 --- a/runtime/jni_env_ext.cc +++ b/runtime/jni_env_ext.cc @@ -29,6 +29,7 @@ #include "mirror/object-inl.h" #include "nth_caller_visitor.h" #include "thread-inl.h" +#include "thread_list.h" namespace art { @@ -37,6 +38,8 @@ using android::base::StringPrintf; static constexpr size_t kMonitorsInitial = 32; // Arbitrary. static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check. +const JNINativeInterface* JNIEnvExt::table_override_ = nullptr; + // Checking "locals" requires the mutator lock, but at creation time we're really only interested // in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged // with NO_THREAD_SAFETY_ANALYSIS. @@ -78,10 +81,10 @@ JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) runtime_deleted(false), critical(0), monitors("monitors", kMonitorsInitial, kMonitorsMax) { - functions = unchecked_functions = GetJniNativeInterface(); - if (vm->IsCheckJniEnabled()) { - SetCheckJniEnabled(true); - } + MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); + check_jni = vm->IsCheckJniEnabled(); + functions = GetFunctionTable(check_jni); + unchecked_functions = GetJniNativeInterface(); } void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() { @@ -107,7 +110,12 @@ void JNIEnvExt::DeleteLocalRef(jobject obj) { void JNIEnvExt::SetCheckJniEnabled(bool enabled) { check_jni = enabled; - functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface(); + MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); + functions = GetFunctionTable(enabled); + // Check whether this is a no-op because of override. + if (enabled && JNIEnvExt::table_override_ != nullptr) { + LOG(WARNING) << "Enabling CheckJNI after a JNIEnv function table override is not functional."; + } } void JNIEnvExt::DumpReferenceTables(std::ostream& os) { @@ -269,4 +277,33 @@ void JNIEnvExt::CheckNoHeldMonitors() { } } +static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED) + REQUIRES(Locks::jni_function_table_lock_) { + JNIEnvExt* env = thread->GetJniEnv(); + bool check_jni = env->check_jni; + env->functions = JNIEnvExt::GetFunctionTable(check_jni); +} + +void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) { + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_); + + JNIEnvExt::table_override_ = table_override; + + // See if we have a runtime. Note: we cannot run other code (like JavaVMExt's CheckJNI install + // code), as we'd have to recursively lock the mutex. + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr); + } +} + +const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) { + const JNINativeInterface* override = JNIEnvExt::table_override_; + if (override != nullptr) { + return override; + } + return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface(); +} + } // namespace art diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h index 5cca0aef9b..4004c457b5 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -43,7 +43,7 @@ struct JNIEnvExt : public JNIEnv { void DumpReferenceTables(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); - void SetCheckJniEnabled(bool enabled); + void SetCheckJniEnabled(bool enabled) REQUIRES(!Locks::jni_function_table_lock_); void PushFrame(int capacity) REQUIRES_SHARED(Locks::mutator_lock_); void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_); @@ -104,10 +104,27 @@ struct JNIEnvExt : public JNIEnv { // Set the functions to the runtime shutdown functions. void SetFunctionsToRuntimeShutdownFunctions(); + // Set the function table override. This will install the override (or original table, if null) + // to all threads. + // Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI. + // After overriding the JNI function table, CheckJNI toggling is ignored. + static void SetTableOverride(const JNINativeInterface* table_override) + REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_); + + // Return either the regular, or the CheckJNI function table. Will return table_override_ instead + // if it is not null. + static const JNINativeInterface* GetFunctionTable(bool check_jni) + REQUIRES(Locks::jni_function_table_lock_); + private: + // Override of function tables. This applies to both default as well as instrumented (CheckJNI) + // function tables. + static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_); + // The constructor should not be called directly. It may leave the object in an erroneous state, // and the result needs to be checked. - JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg); + JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg) + REQUIRES(!Locks::jni_function_table_lock_); // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI // to ensure that only monitors locked in this native frame are being unlocked, and that at diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 4da5e23502..08d1eeb95d 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -2346,4 +2346,39 @@ TEST_F(JniInternalTest, JNIEnvExtOffsets) { EXPECT_EQ(segment_state_now, segment_state_computed); } +static size_t gGlobalRefCount = 0; +static const JNINativeInterface* gOriginalEnv = nullptr; + +static jobject CountNewGlobalRef(JNIEnv* env, jobject o) { + ++gGlobalRefCount; + return gOriginalEnv->NewGlobalRef(env, o); +} + +// Test the table override. +TEST_F(JniInternalTest, JNIEnvExtTableOverride) { + JNINativeInterface env_override; + memcpy(&env_override, env_->functions, sizeof(JNINativeInterface)); + + gOriginalEnv = env_->functions; + env_override.NewGlobalRef = CountNewGlobalRef; + gGlobalRefCount = 0; + + jclass local = env_->FindClass("java/lang/Object"); + ASSERT_TRUE(local != nullptr); + + // Set the table, add a global ref, see whether the counter increases. + JNIEnvExt::SetTableOverride(&env_override); + + jobject global = env_->NewGlobalRef(local); + EXPECT_EQ(1u, gGlobalRefCount); + env_->DeleteGlobalRef(global); + + // Reset + JNIEnvExt::SetTableOverride(nullptr); + + jobject global2 = env_->NewGlobalRef(local); + EXPECT_EQ(1u, gGlobalRefCount); + env_->DeleteGlobalRef(global2); +} + } // namespace art diff --git a/runtime/memory_region.h b/runtime/memory_region.h index f018c1f4c1..fe3f917c76 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -148,14 +148,14 @@ class MemoryRegion FINAL : public ValueObject { void CopyFrom(size_t offset, const MemoryRegion& from) const; // Compute a sub memory region based on an existing one. - MemoryRegion Subregion(uintptr_t offset, uintptr_t size_in) const { + ALWAYS_INLINE MemoryRegion Subregion(uintptr_t offset, uintptr_t size_in) const { CHECK_GE(this->size(), size_in); CHECK_LE(offset, this->size() - size_in); return MemoryRegion(reinterpret_cast<void*>(start() + offset), size_in); } // Compute an extended memory region based on an existing one. - void Extend(const MemoryRegion& region, uintptr_t extra) { + ALWAYS_INLINE void Extend(const MemoryRegion& region, uintptr_t extra) { pointer_ = region.pointer(); size_ = (region.size() + extra); } diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 7c6a710cef..efd949e031 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -113,6 +113,11 @@ void ClassExt::SetVerifyError(ObjPtr<Object> err) { } } +void ClassExt::SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_), bytes); +} + void ClassExt::SetClass(ObjPtr<Class> dalvik_system_ClassExt) { CHECK(dalvik_system_ClassExt != nullptr); dalvik_system_ClassExt_ = GcRoot<Class>(dalvik_system_ClassExt); diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index 91046314db..ad8a61b676 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -61,6 +61,12 @@ class MANAGED ClassExt : public Object { return GetFieldObject<PointerArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_)); } + ByteArray* GetOriginalDexFileBytes() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<ByteArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_)); + } + + void SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) REQUIRES_SHARED(Locks::mutator_lock_); + void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches) REQUIRES_SHARED(Locks::mutator_lock_); @@ -80,7 +86,7 @@ class MANAGED ClassExt : public Object { HeapReference<PointerArray> obsolete_methods_; - HeapReference<DexCache> original_dex_cache_; + HeapReference<ByteArray> original_dex_file_bytes_; // The saved verification error of this class. HeapReference<Object> verify_error_; diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 916f1cf1ef..8f978e122c 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -35,7 +35,6 @@ class DexCacheMethodHandlesTest : public DexCacheTest { protected: virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonRuntimeTest::SetUpRuntimeOptions(options); - options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr)); } }; diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index d607040486..978cc32320 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -195,6 +195,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( // Step 5: Construct the EmulatedStackFrame object. Handle<EmulatedStackFrame> sf(hs.NewHandle( ObjPtr<EmulatedStackFrame>::DownCast(StaticClass()->AllocObject(self)))); + sf->SetFieldObject<false>(CallsiteTypeOffset(), caller_type.Get()); sf->SetFieldObject<false>(TypeOffset(), callee_type.Get()); sf->SetFieldObject<false>(ReferencesOffset(), references.Get()); sf->SetFieldObject<false>(StackFrameOffset(), stack_frame.Get()); diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index d83a53652b..ddd84a167d 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -81,6 +81,10 @@ class MANAGED EmulatedStackFrame : public Object { OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, stack_frame_)); } + static MemberOffset CallsiteTypeOffset() { + return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, callsite_type_)); + } + static MemberOffset TypeOffset() { return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, type_)); } @@ -93,6 +97,7 @@ class MANAGED EmulatedStackFrame : public Object { return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, stack_frame_)); } + HeapReference<mirror::MethodType> callsite_type_; HeapReference<mirror::ObjectArray<mirror::Object>> references_; HeapReference<mirror::ByteArray> stack_frame_; HeapReference<mirror::MethodType> type_; diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index a6f56aee42..6a4ec9dca7 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -306,23 +306,6 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { } -TEST_F(ObjectTest, CheckAndAllocArrayFromCode) { - // pretend we are trying to call 'new char[3]' from String.toCharArray - ScopedObjectAccess soa(Thread::Current()); - Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;"); - ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize); - const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I"); - ASSERT_TRUE(type_id != nullptr); - dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); - Object* array = CheckAndAllocArrayFromCodeInstrumented( - type_idx, 3, sort, Thread::Current(), false, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - EXPECT_TRUE(array->IsArrayInstance()); - EXPECT_EQ(3, array->AsArray()->GetLength()); - EXPECT_TRUE(array->GetClass()->IsArrayClass()); - EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive()); -} - TEST_F(ObjectTest, CreateMultiArray) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 3341f531e4..5438a6ddb4 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -428,6 +428,10 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT } auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc( soa.Self(), mirror::Method::ArrayClass(), num_methods)); + if (ret.Get() == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } num_methods = 0; for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) { auto modifiers = m.GetAccessFlags(); diff --git a/runtime/oat.cc b/runtime/oat.cc index 1a07cdc7f3..d14b399a9a 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -473,6 +473,10 @@ bool OatHeader::IsDebuggable() const { return IsKeyEnabled(OatHeader::kDebuggableKey); } +bool OatHeader::IsConcurrentCopying() const { + return IsKeyEnabled(OatHeader::kConcurrentCopying); +} + bool OatHeader::IsNativeDebuggable() const { return IsKeyEnabled(OatHeader::kNativeDebuggableKey); } diff --git a/runtime/oat.h b/runtime/oat.h index dc103e2b52..953b445e4e 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '9', '5', '\0' }; // alloc entrypoints change + static constexpr uint8_t kOatVersion[] = { '1', '0', '2', '\0' }; // Enabling CC static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; @@ -43,6 +43,7 @@ class PACKED(4) OatHeader { static constexpr const char* kCompilerFilter = "compiler-filter"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPathKey = "bootclasspath"; + static constexpr const char* kConcurrentCopying = "concurrent-copying"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; @@ -112,6 +113,7 @@ class PACKED(4) OatHeader { bool IsDebuggable() const; bool IsNativeDebuggable() const; CompilerFilter::Filter GetCompilerFilter() const; + bool IsConcurrentCopying() const; private: bool KeyHasValue(const char* key, const char* value, size_t value_size) const; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 38df427ed1..d47f1b5611 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -323,8 +323,10 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { } PointerSize pointer_size = GetInstructionSetPointerSize(GetOatHeader().GetInstructionSet()); - uint8_t* dex_cache_arrays = bss_begin_; - uint8_t* dex_cache_arrays_end = (bss_roots_ != nullptr) ? bss_roots_ : bss_end_; + uint8_t* dex_cache_arrays = (bss_begin_ == bss_roots_) ? nullptr : bss_begin_; + uint8_t* dex_cache_arrays_end = + (bss_begin_ == bss_roots_) ? nullptr : (bss_roots_ != nullptr) ? bss_roots_ : bss_end_; + DCHECK_EQ(dex_cache_arrays != nullptr, dex_cache_arrays_end != nullptr); uint32_t dex_file_count = GetOatHeader().GetDexFileCount(); oat_dex_files_storage_.reserve(dex_file_count); for (size_t i = 0; i < dex_file_count; i++) { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index f12a5e7742..8554fa2693 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -304,6 +304,13 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { } OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { + // Verify the ART_USE_READ_BARRIER state. + const bool is_cc = file.GetOatHeader().IsConcurrentCopying(); + constexpr bool kRuntimeIsCC = kUseReadBarrier; + if (is_cc != kRuntimeIsCC) { + return kOatCannotOpen; + } + // Verify the dex checksum. // Note: GetOatDexFile will return null if the dex checksum doesn't match // what we provide, which verifies the primary dex checksum for us. @@ -903,4 +910,3 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() { return std::unique_ptr<OatFile>(); } } // namespace art - diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index afa804c08c..84eacde8b7 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -152,15 +152,17 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { } } - 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()); + 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()); + } } } } diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 4bd21b4c2f..a731c17d9e 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -21,15 +21,21 @@ cc_defaults { "object_tagging.cc", "OpenjdkJvmTi.cc", "ti_class.cc", + "ti_class_definition.cc", "ti_field.cc", "ti_heap.cc", + "ti_jni.cc", "ti_method.cc", "ti_monitor.cc", "ti_object.cc", + "ti_phase.cc", "ti_properties.cc", + "ti_search.cc", "ti_stack.cc", "ti_redefine.cc", "ti_thread.cc", + "ti_threadgroup.cc", + "ti_timers.cc", "transform.cc"], include_dirs: ["art/runtime"], shared_libs: [ diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 2629c9fc07..fcedd4ee4c 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -30,6 +30,7 @@ */ #include <string> +#include <type_traits> #include <vector> #include <jni.h> @@ -37,6 +38,7 @@ #include "openjdkjvmti/jvmti.h" #include "art_jvmti.h" +#include "base/logging.h" #include "base/mutex.h" #include "events-inl.h" #include "jni_env_ext-inl.h" @@ -49,13 +51,18 @@ #include "ti_class.h" #include "ti_field.h" #include "ti_heap.h" +#include "ti_jni.h" #include "ti_method.h" #include "ti_monitor.h" #include "ti_object.h" +#include "ti_phase.h" #include "ti_properties.h" #include "ti_redefine.h" +#include "ti_search.h" #include "ti_stack.h" #include "ti_thread.h" +#include "ti_threadgroup.h" +#include "ti_timers.h" #include "transform.h" // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton @@ -126,7 +133,7 @@ class JvmtiFunctions { } static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::GetAllThreads(env, threads_count_ptr, threads_ptr); } static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread) { @@ -188,27 +195,27 @@ class JvmtiFunctions { jvmtiStartFunction proc, const void* arg, jint priority) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::RunAgentThread(env, thread, proc, arg, priority); } static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::SetThreadLocalStorage(env, thread, data); } static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr); } static jvmtiError GetTopThreadGroups(jvmtiEnv* env, jint* group_count_ptr, jthreadGroup** groups_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr); } static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr); } static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, @@ -217,7 +224,12 @@ class JvmtiFunctions { jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetThreadGroupChildren(env, + group, + thread_count_ptr, + threads_ptr, + group_count_ptr, + groups_ptr); } static jvmtiError GetStackTrace(jvmtiEnv* env, @@ -246,11 +258,15 @@ class JvmtiFunctions { const jthread* thread_list, jint max_frame_count, jvmtiStackInfo** stack_info_ptr) { - return ERR(NOT_IMPLEMENTED); + return StackUtil::GetThreadListStackTraces(env, + thread_count, + thread_list, + max_frame_count, + stack_info_ptr); } static jvmtiError GetFrameCount(jvmtiEnv* env, jthread thread, jint* count_ptr) { - return ERR(NOT_IMPLEMENTED); + return StackUtil::GetFrameCount(env, thread, count_ptr); } static jvmtiError PopFrame(jvmtiEnv* env, jthread thread) { @@ -262,7 +278,7 @@ class JvmtiFunctions { jint depth, jmethodID* method_ptr, jlocation* location_ptr) { - return ERR(NOT_IMPLEMENTED); + return StackUtil::GetFrameLocation(env, thread, depth, method_ptr, location_ptr); } static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) { @@ -531,7 +547,7 @@ class JvmtiFunctions { jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassLoaderClasses(env, initiating_loader, class_count_ptr, classes_ptr); } static jvmtiError GetClassSignature(jvmtiEnv* env, @@ -578,7 +594,7 @@ class JvmtiFunctions { jclass klass, jint* minor_version_ptr, jint* major_version_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassVersionNumbers(env, klass, minor_version_ptr, major_version_ptr); } static jvmtiError GetConstantPool(jvmtiEnv* env, @@ -616,13 +632,33 @@ class JvmtiFunctions { } static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) { - return ERR(NOT_IMPLEMENTED); + std::string error_msg; + jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env), + art::Runtime::Current(), + art::Thread::Current(), + class_count, + classes, + &error_msg); + if (res != OK) { + LOG(WARNING) << "FAILURE TO RETRANFORM " << error_msg; + } + return res; } static jvmtiError RedefineClasses(jvmtiEnv* env, jint class_count, const jvmtiClassDefinition* class_definitions) { - return ERR(NOT_IMPLEMENTED); + std::string error_msg; + jvmtiError res = Redefiner::RedefineClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env), + art::Runtime::Current(), + art::Thread::Current(), + class_count, + class_definitions, + &error_msg); + if (res != OK) { + LOG(WARNING) << "FAILURE TO REDEFINE " << error_msg; + } + return res; } static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr) { @@ -778,11 +814,11 @@ class JvmtiFunctions { } static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) { - return ERR(NOT_IMPLEMENTED); + return JNIUtil::SetJNIFunctionTable(env, function_table); } static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) { - return ERR(NOT_IMPLEMENTED); + return JNIUtil::GetJNIFunctionTable(env, function_table); } // TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching @@ -833,7 +869,8 @@ class JvmtiFunctions { } } - return gEventHandler.SetEvent(ArtJvmTiEnv::AsArtJvmTiEnv(env), art_thread, event_type, mode); + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode); } static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) { @@ -879,11 +916,15 @@ class JvmtiFunctions { ENSURE_NON_NULL(capabilities_ptr); ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env); jvmtiError ret = OK; + jvmtiCapabilities changed; #define ADD_CAPABILITY(e) \ do { \ if (capabilities_ptr->e == 1) { \ if (kPotentialCapabilities.e == 1) { \ - art_env->capabilities.e = 1;\ + if (art_env->capabilities.e != 1) { \ + art_env->capabilities.e = 1; \ + changed.e = 1; \ + }\ } else { \ ret = ERR(NOT_AVAILABLE); \ } \ @@ -932,6 +973,9 @@ class JvmtiFunctions { ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events); ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events); #undef ADD_CAPABILITY + gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), + changed, + /*added*/true); return ret; } @@ -940,10 +984,14 @@ class JvmtiFunctions { ENSURE_VALID_ENV(env); ENSURE_NON_NULL(capabilities_ptr); ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env); + jvmtiCapabilities changed; #define DEL_CAPABILITY(e) \ do { \ if (capabilities_ptr->e == 1) { \ - art_env->capabilities.e = 0;\ + if (art_env->capabilities.e == 1) { \ + art_env->capabilities.e = 0;\ + changed.e = 1; \ + } \ } \ } while (false) @@ -989,6 +1037,9 @@ class JvmtiFunctions { DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events); DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events); #undef DEL_CAPABILITY + gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), + changed, + /*added*/false); return OK; } @@ -1017,23 +1068,23 @@ class JvmtiFunctions { } static jvmtiError GetTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) { - return ERR(NOT_IMPLEMENTED); + return TimerUtil::GetTimerInfo(env, info_ptr); } static jvmtiError GetTime(jvmtiEnv* env, jlong* nanos_ptr) { - return ERR(NOT_IMPLEMENTED); + return TimerUtil::GetTime(env, nanos_ptr); } static jvmtiError GetAvailableProcessors(jvmtiEnv* env, jint* processor_count_ptr) { - return ERR(NOT_IMPLEMENTED); + return TimerUtil::GetAvailableProcessors(env, processor_count_ptr); } static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) { - return ERR(NOT_IMPLEMENTED); + return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment); } static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) { - return ERR(NOT_IMPLEMENTED); + return SearchUtil::AddToSystemClassLoaderSearch(env, segment); } static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) { @@ -1049,11 +1100,12 @@ class JvmtiFunctions { } static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) { - return ERR(NOT_IMPLEMENTED); + return PhaseUtil::GetPhase(env, phase_ptr); } static jvmtiError DisposeEnvironment(jvmtiEnv* env) { ENSURE_VALID_ENV(env); + gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env)); delete env; return OK; } @@ -1154,112 +1206,66 @@ class JvmtiFunctions { } static jvmtiError SetVerboseFlag(jvmtiEnv* env, jvmtiVerboseFlag flag, jboolean value) { - return ERR(NOT_IMPLEMENTED); - } - - static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) { - return ERR(NOT_IMPLEMENTED); - } + if (flag == jvmtiVerboseFlag::JVMTI_VERBOSE_OTHER) { + // OTHER is special, as it's 0, so can't do a bit check. + bool val = (value == JNI_TRUE) ? true : false; + + art::gLogVerbosity.collector = val; + art::gLogVerbosity.compiler = val; + art::gLogVerbosity.deopt = val; + art::gLogVerbosity.heap = val; + art::gLogVerbosity.jdwp = val; + art::gLogVerbosity.jit = val; + art::gLogVerbosity.monitor = val; + art::gLogVerbosity.oat = val; + art::gLogVerbosity.profiler = val; + art::gLogVerbosity.signals = val; + art::gLogVerbosity.simulator = val; + art::gLogVerbosity.startup = val; + art::gLogVerbosity.third_party_jni = val; + art::gLogVerbosity.threads = val; + art::gLogVerbosity.verifier = val; + art::gLogVerbosity.image = val; + + // Note: can't switch systrace_lock_logging. That requires changing entrypoints. + + art::gLogVerbosity.agents = val; + } else { + // Spec isn't clear whether "flag" is a mask or supposed to be single. We implement the mask + // semantics. + constexpr std::underlying_type<jvmtiVerboseFlag>::type kMask = + jvmtiVerboseFlag::JVMTI_VERBOSE_GC | + jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS | + jvmtiVerboseFlag::JVMTI_VERBOSE_JNI; + if ((flag & ~kMask) != 0) { + return ERR(ILLEGAL_ARGUMENT); + } - // TODO Remove this once events are working. - static jvmtiError RetransformClassWithHook(jvmtiEnv* env, - jclass klass, - jvmtiEventClassFileLoadHook hook) { - std::vector<jclass> classes; - classes.push_back(klass); - return RetransformClassesWithHook(reinterpret_cast<ArtJvmTiEnv*>(env), classes, hook); - } - - static jvmtiError RedefineClassDirect(ArtJvmTiEnv* env, - jclass klass, - jint dex_size, - unsigned char* dex_file) { - if (!IsValidEnv(env)) { - return ERR(INVALID_ENVIRONMENT); - } - jvmtiError ret = OK; - std::string location; - if ((ret = GetClassLocation(env, klass, &location)) != OK) { - // TODO Do something more here? Maybe give log statements? - return ret; - } - std::string error; - ret = Redefiner::RedefineClass(env, - art::Runtime::Current(), - art::Thread::Current(), - klass, - location, - dex_size, - reinterpret_cast<uint8_t*>(dex_file), - &error); - if (ret != OK) { - LOG(WARNING) << "FAILURE TO REDEFINE " << error; - } - return ret; - } + bool val = (value == JNI_TRUE) ? true : false; - // TODO This will be called by the event handler for the art::ti Event Load Event - static jvmtiError RetransformClassesWithHook(ArtJvmTiEnv* env, - const std::vector<jclass>& classes, - jvmtiEventClassFileLoadHook hook) { - if (!IsValidEnv(env)) { - return ERR(INVALID_ENVIRONMENT); - } - jvmtiError res = OK; - std::string error; - for (jclass klass : classes) { - JNIEnv* jni_env = nullptr; - jobject loader = nullptr; - std::string name; - jobject protection_domain = nullptr; - jint data_len = 0; - unsigned char* dex_data = nullptr; - jvmtiError ret = OK; - std::string location; - if ((ret = GetTransformationData(env, - klass, - /*out*/&location, - /*out*/&jni_env, - /*out*/&loader, - /*out*/&name, - /*out*/&protection_domain, - /*out*/&data_len, - /*out*/&dex_data)) != OK) { - // TODO Do something more here? Maybe give log statements? - return ret; + if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_GC) != 0) { + art::gLogVerbosity.gc = val; } - jint new_data_len = 0; - unsigned char* new_dex_data = nullptr; - hook(env, - jni_env, - klass, - loader, - name.c_str(), - protection_domain, - data_len, - dex_data, - /*out*/&new_data_len, - /*out*/&new_dex_data); - // Check if anything actually changed. - if ((new_data_len != 0 || new_dex_data != nullptr) && new_dex_data != dex_data) { - res = Redefiner::RedefineClass(env, - art::Runtime::Current(), - art::Thread::Current(), - klass, - location, - new_data_len, - new_dex_data, - &error); - env->Deallocate(new_dex_data); + + if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS) != 0) { + art::gLogVerbosity.class_linker = val; } - // Deallocate the old dex data. - env->Deallocate(dex_data); - if (res != OK) { - LOG(ERROR) << "FAILURE TO REDEFINE " << error; - return res; + + if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_JNI) != 0) { + art::gLogVerbosity.jni = val; } } - return OK; + + return ERR(NONE); + } + + static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) { + // Report BCI as jlocation format. We report dex bytecode indices. + if (format_ptr == nullptr) { + return ERR(NULL_POINTER); + } + *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI; + return ERR(NONE); } }; @@ -1296,22 +1302,25 @@ static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { // The plugin initialization function. This adds the jvmti environment. extern "C" bool ArtPlugin_Initialize() { art::Runtime* runtime = art::Runtime::Current(); + + if (runtime->IsStarted()) { + PhaseUtil::SetToLive(); + } else { + PhaseUtil::SetToOnLoad(); + } + PhaseUtil::Register(&gEventHandler); + runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); runtime->AddSystemWeakHolder(&gObjectTagTable); + return true; } // The actual struct holding all of the entrypoints into the jvmti interface. const jvmtiInterface_1 gJvmtiInterface = { - // SPECIAL FUNCTION: RetransformClassWithHook Is normally reserved1 - // TODO Remove once we have events working. - reinterpret_cast<void*>(JvmtiFunctions::RetransformClassWithHook), - // nullptr, // reserved1 + nullptr, // reserved1 JvmtiFunctions::SetEventNotificationMode, - // SPECIAL FUNCTION: RedefineClassDirect Is normally reserved3 - // TODO Remove once we have events working. - reinterpret_cast<void*>(JvmtiFunctions::RedefineClassDirect), - // nullptr, // reserved3 + nullptr, // reserved3 JvmtiFunctions::GetAllThreads, JvmtiFunctions::SuspendThread, JvmtiFunctions::ResumeThread, diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 5eadc5a8e0..256c3a6cec 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -36,6 +36,7 @@ #include <jni.h> +#include "base/array_slice.h" #include "base/casts.h" #include "base/logging.h" #include "base/macros.h" @@ -47,6 +48,7 @@ namespace openjdkjvmti { extern const jvmtiInterface_1 gJvmtiInterface; +extern EventHandler gEventHandler; // A structure that is a jvmtiEnv with additional information for the runtime. struct ArtJvmTiEnv : public jvmtiEnv { @@ -134,7 +136,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_get_current_contended_monitor = 0, .can_get_monitor_info = 0, .can_pop_frame = 0, - .can_redefine_classes = 0, + .can_redefine_classes = 1, .can_signal_thread = 0, .can_get_source_file_name = 0, .can_get_line_numbers = 0, @@ -162,7 +164,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_get_owned_monitor_stack_depth_info = 0, .can_get_constant_pool = 0, .can_set_native_method_prefix = 0, - .can_retransform_classes = 0, + .can_retransform_classes = 1, .can_retransform_any_class = 0, .can_generate_resource_exhaustion_heap_events = 0, .can_generate_resource_exhaustion_threads_events = 0, diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index d0272010d4..21ec731ba5 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -17,14 +17,28 @@ #ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ #define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ +#include <array> + #include "events.h" #include "art_jvmti.h" namespace openjdkjvmti { +static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { + if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) { + if (env->capabilities.can_retransform_classes) { + return ArtJvmtiEvent::kClassFileLoadHookRetransformable; + } else { + return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; + } + } else { + return static_cast<ArtJvmtiEvent>(e); + } +} + template <typename FnType> -ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) { +ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) { if (env->event_callbacks == nullptr) { return nullptr; } @@ -33,84 +47,166 @@ ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent eve // function. switch (event) { - case JVMTI_EVENT_VM_INIT: + case ArtJvmtiEvent::kVmInit: return reinterpret_cast<FnType*>(env->event_callbacks->VMInit); - case JVMTI_EVENT_VM_DEATH: + case ArtJvmtiEvent::kVmDeath: return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath); - case JVMTI_EVENT_THREAD_START: + case ArtJvmtiEvent::kThreadStart: return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart); - case JVMTI_EVENT_THREAD_END: + case ArtJvmtiEvent::kThreadEnd: return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd); - case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK: + case ArtJvmtiEvent::kClassFileLoadHookRetransformable: + case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable: return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook); - case JVMTI_EVENT_CLASS_LOAD: + case ArtJvmtiEvent::kClassLoad: return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad); - case JVMTI_EVENT_CLASS_PREPARE: + case ArtJvmtiEvent::kClassPrepare: return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare); - case JVMTI_EVENT_VM_START: + case ArtJvmtiEvent::kVmStart: return reinterpret_cast<FnType*>(env->event_callbacks->VMStart); - case JVMTI_EVENT_EXCEPTION: + case ArtJvmtiEvent::kException: return reinterpret_cast<FnType*>(env->event_callbacks->Exception); - case JVMTI_EVENT_EXCEPTION_CATCH: + case ArtJvmtiEvent::kExceptionCatch: return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch); - case JVMTI_EVENT_SINGLE_STEP: + case ArtJvmtiEvent::kSingleStep: return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep); - case JVMTI_EVENT_FRAME_POP: + case ArtJvmtiEvent::kFramePop: return reinterpret_cast<FnType*>(env->event_callbacks->FramePop); - case JVMTI_EVENT_BREAKPOINT: + case ArtJvmtiEvent::kBreakpoint: return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint); - case JVMTI_EVENT_FIELD_ACCESS: + case ArtJvmtiEvent::kFieldAccess: return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess); - case JVMTI_EVENT_FIELD_MODIFICATION: + case ArtJvmtiEvent::kFieldModification: return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification); - case JVMTI_EVENT_METHOD_ENTRY: + case ArtJvmtiEvent::kMethodEntry: return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry); - case JVMTI_EVENT_METHOD_EXIT: + case ArtJvmtiEvent::kMethodExit: return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit); - case JVMTI_EVENT_NATIVE_METHOD_BIND: + case ArtJvmtiEvent::kNativeMethodBind: return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind); - case JVMTI_EVENT_COMPILED_METHOD_LOAD: + case ArtJvmtiEvent::kCompiledMethodLoad: return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad); - case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: + case ArtJvmtiEvent::kCompiledMethodUnload: return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload); - case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: + case ArtJvmtiEvent::kDynamicCodeGenerated: return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated); - case JVMTI_EVENT_DATA_DUMP_REQUEST: + case ArtJvmtiEvent::kDataDumpRequest: return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest); - case JVMTI_EVENT_MONITOR_WAIT: + case ArtJvmtiEvent::kMonitorWait: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait); - case JVMTI_EVENT_MONITOR_WAITED: + case ArtJvmtiEvent::kMonitorWaited: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited); - case JVMTI_EVENT_MONITOR_CONTENDED_ENTER: + case ArtJvmtiEvent::kMonitorContendedEnter: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter); - case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED: + case ArtJvmtiEvent::kMonitorContendedEntered: return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered); - case JVMTI_EVENT_RESOURCE_EXHAUSTED: + case ArtJvmtiEvent::kResourceExhausted: return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted); - case JVMTI_EVENT_GARBAGE_COLLECTION_START: + case ArtJvmtiEvent::kGarbageCollectionStart: return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart); - case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: + case ArtJvmtiEvent::kGarbageCollectionFinish: return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish); - case JVMTI_EVENT_OBJECT_FREE: + case ArtJvmtiEvent::kObjectFree: return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree); - case JVMTI_EVENT_VM_OBJECT_ALLOC: + case ArtJvmtiEvent::kVmObjectAlloc: return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc); } return nullptr; } template <typename ...Args> -inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) { - using FnType = void(jvmtiEnv*, Args...); - for (ArtJvmTiEnv* env : envs) { - bool dispatch = env->event_masks.global_event_mask.Test(event); +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!"; +} - if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) { - EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); - dispatch = mask != nullptr && mask->Test(event); +// TODO Locking of some type! +template <> +inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, + ArtJvmtiEvent event, + 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 { + 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 */); + 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); + callback(env, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + current_len, + current_class_data, + &new_len, + &new_data); + if (new_data != nullptr && new_data != current_class_data) { + // Destroy the data the last transformer made. We skip this if the previous state was the + // initial one since we don't know here which jvmtiEnv allocated it. + // NB Currently this doesn't matter since all allocations just go to malloc but in the + // future we might have jvmtiEnv's keep track of their allocations for leak-checking. + if (last_env != nullptr) { + last_env->Deallocate(current_class_data); + } + last_env = env; + current_class_data = new_data; + current_len = new_len; + } } + } + if (last_env != nullptr) { + *new_class_data_len = current_len; + *new_class_data = current_class_data; + } +} + +template <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...); + } +} - if (dispatch) { +// 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 (callback != nullptr) { (*callback)(env, args...); @@ -119,6 +215,52 @@ inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, A } } +inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event, + ArtJvmTiEnv* env, + art::Thread* thread) { + bool dispatch = env->event_masks.global_event_mask.Test(event); + + if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) { + EventMask* mask = env->event_masks.GetEventMaskOrNull(thread); + dispatch = mask != nullptr && mask->Test(event); + } + return dispatch; +} + +inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { + bool union_value = false; + for (const ArtJvmTiEnv* stored_env : envs) { + union_value |= stored_env->event_masks.global_event_mask.Test(event); + union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); + if (union_value) { + break; + } + } + global_mask.Set(event, union_value); +} + +inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added) { + ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable + : ArtJvmtiEvent::kClassFileLoadHookRetransformable; + return caps.can_retransform_classes == 1 && + IsEventEnabledAnywhere(event) && + env->event_masks.IsEnabledAnywhere(event); +} + +inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added) { + if (UNLIKELY(NeedsEventUpdate(env, caps, added))) { + env->event_masks.HandleChangedCapabilities(caps, added); + if (caps.can_retransform_classes == 1) { + RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable); + RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable); + } + } +} + } // namespace openjdkjvmti #endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_ diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index 12692a168d..7182055b7d 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -47,6 +47,10 @@ namespace openjdkjvmti { +bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) { + return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event); +} + EventMask& EventMasks::GetEventMask(art::Thread* thread) { if (thread == nullptr) { return global_event_mask; @@ -83,7 +87,7 @@ EventMask* EventMasks::GetEventMaskOrNull(art::Thread* thread) { } -void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) { +void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) { DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event); if (thread != nullptr) { @@ -91,7 +95,7 @@ void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) { } } -void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) { +void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) { DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event, false); if (thread != nullptr) { @@ -107,20 +111,61 @@ void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) { } } +void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added) { + if (UNLIKELY(caps.can_retransform_classes == 1)) { + // If we are giving this env the retransform classes cap we need to switch all events of + // NonTransformable to Transformable and vice versa. + ArtJvmtiEvent to_remove = caps_added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable + : ArtJvmtiEvent::kClassFileLoadHookRetransformable; + ArtJvmtiEvent to_add = caps_added ? ArtJvmtiEvent::kClassFileLoadHookRetransformable + : ArtJvmtiEvent::kClassFileLoadHookNonRetransformable; + if (global_event_mask.Test(to_remove)) { + CHECK(!global_event_mask.Test(to_add)); + global_event_mask.Set(to_remove, false); + global_event_mask.Set(to_add, true); + } + + if (unioned_thread_event_mask.Test(to_remove)) { + CHECK(!unioned_thread_event_mask.Test(to_add)); + unioned_thread_event_mask.Set(to_remove, false); + unioned_thread_event_mask.Set(to_add, true); + } + for (auto thread_mask : thread_event_masks) { + if (thread_mask.second.Test(to_remove)) { + CHECK(!thread_mask.second.Test(to_add)); + thread_mask.second.Set(to_remove, false); + thread_mask.second.Set(to_add, true); + } + } + } +} + void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { envs.push_back(env); } -static bool IsThreadControllable(jvmtiEvent event) { +void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { + auto it = std::find(envs.begin(), envs.end(), env); + if (it != envs.end()) { + envs.erase(it); + for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal); + i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal); + ++i) { + RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i)); + } + } +} + +static bool IsThreadControllable(ArtJvmtiEvent event) { switch (event) { - case JVMTI_EVENT_VM_INIT: - case JVMTI_EVENT_VM_START: - case JVMTI_EVENT_VM_DEATH: - case JVMTI_EVENT_THREAD_START: - case JVMTI_EVENT_COMPILED_METHOD_LOAD: - case JVMTI_EVENT_COMPILED_METHOD_UNLOAD: - case JVMTI_EVENT_DYNAMIC_CODE_GENERATED: - case JVMTI_EVENT_DATA_DUMP_REQUEST: + case ArtJvmtiEvent::kVmInit: + case ArtJvmtiEvent::kVmStart: + case ArtJvmtiEvent::kVmDeath: + case ArtJvmtiEvent::kThreadStart: + case ArtJvmtiEvent::kCompiledMethodLoad: + case ArtJvmtiEvent::kCompiledMethodUnload: + case ArtJvmtiEvent::kDynamicCodeGenerated: + case ArtJvmtiEvent::kDataDumpRequest: return false; default: @@ -136,7 +181,7 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { DCHECK_EQ(self, art::Thread::Current()); - if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) { + if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kVmObjectAlloc)) { art::StackHandleScope<1> hs(self); auto h = hs.NewHandleWrapper(obj); // jvmtiEventVMObjectAlloc parameters: @@ -162,7 +207,7 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass())); handler_->DispatchEvent(self, - JVMTI_EVENT_VM_OBJECT_ALLOC, + ArtJvmtiEvent::kVmObjectAlloc, jni_env, thread.get(), object.get(), @@ -196,11 +241,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START); + handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart); } void EndPause() OVERRIDE { - handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH); + handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish); } bool IsEnabled() { @@ -221,10 +266,10 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { bool finish_enabled_; }; -static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) { +static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent event, bool enable) { bool old_state = listener->IsEnabled(); - if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) { + if (event == ArtJvmtiEvent::kGarbageCollectionStart) { listener->SetStartEnabled(enable); } else { listener->SetFinishEnabled(enable); @@ -242,14 +287,14 @@ static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent even } // Handle special work for the given event type, if necessary. -void EventHandler::HandleEventType(jvmtiEvent event, bool enable) { +void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { - case JVMTI_EVENT_VM_OBJECT_ALLOC: + case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); return; - case JVMTI_EVENT_GARBAGE_COLLECTION_START: - case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH: + case ArtJvmtiEvent::kGarbageCollectionStart: + case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); return; @@ -260,7 +305,7 @@ void EventHandler::HandleEventType(jvmtiEvent event, bool enable) { jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, art::Thread* thread, - jvmtiEvent event, + ArtJvmtiEvent event, jvmtiEventMode mode) { if (thread != nullptr) { art::ThreadState state = thread->GetState(); @@ -293,17 +338,7 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, DCHECK_EQ(mode, JVMTI_DISABLE); env->event_masks.DisableEvent(thread, event); - - // Gotta recompute the global mask. - bool union_value = false; - for (const ArtJvmTiEnv* stored_env : envs) { - union_value |= stored_env->event_masks.global_event_mask.Test(event); - union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event); - if (union_value) { - break; - } - } - global_mask.Set(event, union_value); + RecalculateGlobalEventMask(event); } bool new_state = global_mask.Test(event); diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h index 07d6bfd4c2..8e246ded8e 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -30,22 +30,76 @@ struct ArtJvmTiEnv; class JvmtiAllocationListener; class JvmtiGcPauseListener; +// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between +// retransformation capable and incapable loading +enum class ArtJvmtiEvent { + kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL, + kVmInit = JVMTI_EVENT_VM_INIT, + kVmDeath = JVMTI_EVENT_VM_DEATH, + kThreadStart = JVMTI_EVENT_THREAD_START, + kThreadEnd = JVMTI_EVENT_THREAD_END, + kClassFileLoadHookNonRetransformable = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + kClassLoad = JVMTI_EVENT_CLASS_LOAD, + kClassPrepare = JVMTI_EVENT_CLASS_PREPARE, + kVmStart = JVMTI_EVENT_VM_START, + kException = JVMTI_EVENT_EXCEPTION, + kExceptionCatch = JVMTI_EVENT_EXCEPTION_CATCH, + kSingleStep = JVMTI_EVENT_SINGLE_STEP, + kFramePop = JVMTI_EVENT_FRAME_POP, + kBreakpoint = JVMTI_EVENT_BREAKPOINT, + kFieldAccess = JVMTI_EVENT_FIELD_ACCESS, + kFieldModification = JVMTI_EVENT_FIELD_MODIFICATION, + kMethodEntry = JVMTI_EVENT_METHOD_ENTRY, + kMethodExit = JVMTI_EVENT_METHOD_EXIT, + kNativeMethodBind = JVMTI_EVENT_NATIVE_METHOD_BIND, + kCompiledMethodLoad = JVMTI_EVENT_COMPILED_METHOD_LOAD, + kCompiledMethodUnload = JVMTI_EVENT_COMPILED_METHOD_UNLOAD, + kDynamicCodeGenerated = JVMTI_EVENT_DYNAMIC_CODE_GENERATED, + kDataDumpRequest = JVMTI_EVENT_DATA_DUMP_REQUEST, + kMonitorWait = JVMTI_EVENT_MONITOR_WAIT, + kMonitorWaited = JVMTI_EVENT_MONITOR_WAITED, + kMonitorContendedEnter = JVMTI_EVENT_MONITOR_CONTENDED_ENTER, + kMonitorContendedEntered = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, + kResourceExhausted = JVMTI_EVENT_RESOURCE_EXHAUSTED, + kGarbageCollectionStart = JVMTI_EVENT_GARBAGE_COLLECTION_START, + kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, + kObjectFree = JVMTI_EVENT_OBJECT_FREE, + kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, + kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, + kMaxEventTypeVal = kClassFileLoadHookRetransformable, +}; + +// Convert a jvmtiEvent into a ArtJvmtiEvent +ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e); + +static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) { + if (UNLIKELY(e == ArtJvmtiEvent::kClassFileLoadHookRetransformable)) { + return JVMTI_EVENT_CLASS_FILE_LOAD_HOOK; + } else { + return static_cast<jvmtiEvent>(e); + } +} + struct EventMask { - static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1; + static constexpr size_t kEventsSize = + static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal) - + static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal) + 1; std::bitset<kEventsSize> bit_set; - static bool EventIsInRange(jvmtiEvent event) { - return event >= JVMTI_MIN_EVENT_TYPE_VAL && event <= JVMTI_MAX_EVENT_TYPE_VAL; + static bool EventIsInRange(ArtJvmtiEvent event) { + return event >= ArtJvmtiEvent::kMinEventTypeVal && event <= ArtJvmtiEvent::kMaxEventTypeVal; } - void Set(jvmtiEvent event, bool value = true) { + void Set(ArtJvmtiEvent event, bool value = true) { DCHECK(EventIsInRange(event)); - bit_set.set(event - JVMTI_MIN_EVENT_TYPE_VAL, value); + bit_set.set(static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal), + value); } - bool Test(jvmtiEvent event) const { + bool Test(ArtJvmtiEvent event) const { DCHECK(EventIsInRange(event)); - return bit_set.test(event - JVMTI_MIN_EVENT_TYPE_VAL); + return bit_set.test( + static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal)); } }; @@ -68,8 +122,13 @@ struct EventMasks { EventMask& GetEventMask(art::Thread* thread); EventMask* GetEventMaskOrNull(art::Thread* thread); - void EnableEvent(art::Thread* thread, jvmtiEvent event); - void DisableEvent(art::Thread* thread, jvmtiEvent event); + void EnableEvent(art::Thread* thread, ArtJvmtiEvent event); + void DisableEvent(art::Thread* thread, ArtJvmtiEvent event); + bool IsEnabledAnywhere(ArtJvmtiEvent event); + // Make any changes to event masks needed for the given capability changes. If caps_added is true + // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the + // set of all capabilities that were removed from the jvmtiEnv. + void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added); }; // Helper class for event handling. @@ -82,20 +141,56 @@ class EventHandler { // enabled, yet. void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); - bool IsEventEnabledAnywhere(jvmtiEvent event) { + // Remove an env. + void RemoveArtJvmTiEnv(ArtJvmTiEnv* env); + + bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { return false; } return global_mask.Test(event); } - jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, jvmtiEvent event, jvmtiEventMode mode); + jvmtiError SetEvent(ArtJvmTiEnv* env, + art::Thread* thread, + ArtJvmtiEvent event, + jvmtiEventMode mode); template <typename ...Args> - ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args); + ALWAYS_INLINE + inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, 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 + // then caps is the set of all capabilities that were removed from the jvmtiEnv. + ALWAYS_INLINE + inline void HandleChangedCapabilities(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added); private: - void HandleEventType(jvmtiEvent event, bool enable); + ALWAYS_INLINE + static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread); + + ALWAYS_INLINE + inline bool NeedsEventUpdate(ArtJvmTiEnv* env, + const jvmtiCapabilities& caps, + bool added); + + // Recalculates the event mask for the given event. + 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> + ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread, + ArtJvmtiEvent event, + Args... args) const; + + void HandleEventType(ArtJvmtiEvent event, bool enable); // List of all JvmTiEnv objects that have been created, in their creation order. std::vector<ArtJvmTiEnv*> envs; diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/jvmti.h index ee708cb193..de07c163fc 100644 --- a/runtime/openjdkjvmti/jvmti.h +++ b/runtime/openjdkjvmti/jvmti.h @@ -74,7 +74,7 @@ typedef jobject jthreadGroup; typedef jlong jlocation; struct _jrawMonitorID; typedef struct _jrawMonitorID *jrawMonitorID; -typedef struct JNINativeInterface_ jniNativeInterface; +typedef struct JNINativeInterface jniNativeInterface; /* Constants */ diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc index b983e79658..94cb46a428 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -177,7 +177,7 @@ bool ObjectTagTable::SetLocked(art::Thread* self, art::mirror::Object* obj, jlon } void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) { - if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) { + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) { SweepImpl<true>(visitor); } else { SweepImpl<false>(visitor); @@ -207,7 +207,7 @@ void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) { } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag); + event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag); } template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull> diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 0d1704ca4d..abcc84925b 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -32,7 +32,10 @@ #include "ti_class.h" #include "art_jvmti.h" +#include "class_table-inl.h" +#include "class_linker.h" #include "jni_internal.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -328,4 +331,121 @@ jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + UNUSED(env, initiating_loader, class_count_ptr, classes_ptr); + + if (class_count_ptr == nullptr || classes_ptr == nullptr) { + return ERR(NULL_POINTER); + } + art::Thread* self = art::Thread::Current(); + if (!self->GetJniEnv()->IsInstanceOf(initiating_loader, + art::WellKnownClasses::java_lang_ClassLoader)) { + return ERR(ILLEGAL_ARGUMENT); + } + if (self->GetJniEnv()->IsInstanceOf(initiating_loader, + art::WellKnownClasses::java_lang_BootClassLoader)) { + // Need to use null for the BootClassLoader. + initiating_loader = nullptr; + } + + art::ScopedObjectAccess soa(self); + art::ObjPtr<art::mirror::ClassLoader> class_loader = + soa.Decode<art::mirror::ClassLoader>(initiating_loader); + + art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); + + art::ReaderMutexLock mu(self, *art::Locks::classlinker_classes_lock_); + + art::ClassTable* class_table = class_linker->ClassTableForClassLoader(class_loader); + if (class_table == nullptr) { + // Nothing loaded. + *class_count_ptr = 0; + *classes_ptr = nullptr; + return ERR(NONE); + } + + struct ClassTableCount { + bool operator()(art::ObjPtr<art::mirror::Class> klass) { + DCHECK(klass != nullptr); + ++count; + return true; + } + + size_t count = 0; + }; + ClassTableCount ctc; + class_table->Visit(ctc); + + if (ctc.count == 0) { + // Nothing loaded. + *class_count_ptr = 0; + *classes_ptr = nullptr; + return ERR(NONE); + } + + unsigned char* data; + jvmtiError data_result = env->Allocate(ctc.count * sizeof(jclass), &data); + if (data_result != ERR(NONE)) { + return data_result; + } + jclass* class_array = reinterpret_cast<jclass*>(data); + + struct ClassTableFill { + bool operator()(art::ObjPtr<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(klass != nullptr); + DCHECK_LT(count, ctc_ref.count); + local_class_array[count++] = soa_ptr->AddLocalReference<jclass>(klass); + return true; + } + + jclass* local_class_array; + const ClassTableCount& ctc_ref; + art::ScopedObjectAccess* soa_ptr; + size_t count; + }; + ClassTableFill ctf = { class_array, ctc, &soa, 0 }; + class_table->Visit(ctf); + DCHECK_EQ(ctc.count, ctf.count); + + *class_count_ptr = ctc.count; + *classes_ptr = class_array; + + return ERR(NONE); +} + +jvmtiError ClassUtil::GetClassVersionNumbers(jvmtiEnv* env ATTRIBUTE_UNUSED, + jclass jklass, + jint* minor_version_ptr, + jint* major_version_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (jklass == nullptr) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass); + if (!jklass_obj->IsClass()) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Class> klass = jklass_obj->AsClass(); + if (klass->IsPrimitive() || klass->IsArrayClass()) { + return ERR(INVALID_CLASS); + } + + if (minor_version_ptr == nullptr || major_version_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + // Note: proxies will show the dex file version of java.lang.reflect.Proxy, as that is + // what their dex cache copies from. + uint32_t version = klass->GetDexFile().GetHeader().GetVersion(); + + *major_version_ptr = static_cast<jint>(version); + *minor_version_ptr = 0; + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h index 577fc8e866..9558894deb 100644 --- a/runtime/openjdkjvmti/ti_class.h +++ b/runtime/openjdkjvmti/ti_class.h @@ -65,8 +65,18 @@ class ClassUtil { static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr); + static jvmtiError GetClassLoaderClasses(jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr); static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr); + + static jvmtiError GetClassVersionNumbers(jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc new file mode 100644 index 0000000000..2c2a79bc58 --- /dev/null +++ b/runtime/openjdkjvmti/ti_class_definition.cc @@ -0,0 +1,55 @@ +/* Copyright (C) 2016 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_class_definition.h" + +#include "dex_file.h" +#include "handle_scope-inl.h" +#include "handle.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "thread.h" + +namespace openjdkjvmti { + +bool ArtClassDefinition::IsModified(art::Thread* self) const { + if (modified) { + return true; + } + // Check if the dex file we want to set is the same as the current one. + art::StackHandleScope<1> hs(self); + art::Handle<art::mirror::Class> h_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass())); + const art::DexFile& cur_dex_file = h_klass->GetDexFile(); + return static_cast<jint>(cur_dex_file.Size()) != dex_len || + memcmp(cur_dex_file.Begin(), dex_data.get(), dex_len) != 0; +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h new file mode 100644 index 0000000000..dbe5da2d63 --- /dev/null +++ b/runtime/openjdkjvmti/ti_class_definition.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_ + +#include "art_jvmti.h" + +namespace openjdkjvmti { + +// A struct that stores data needed for redefining/transforming classes. This structure should only +// even be accessed from a single thread and must not survive past the completion of the +// redefinition/retransformation function that created it. +struct ArtClassDefinition { + public: + jclass klass; + jobject loader; + std::string name; + jobject protection_domain; + jint dex_len; + JvmtiUniquePtr dex_data; + art::ArraySlice<const unsigned char> original_dex_file; + + ArtClassDefinition() = default; + ArtClassDefinition(ArtClassDefinition&& o) = default; + + void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) { + if (new_dex_data == nullptr) { + return; + } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) { + SetModified(); + dex_len = new_dex_len; + dex_data = MakeJvmtiUniquePtr(env, new_dex_data); + } + } + + void SetModified() { + modified = true; + } + + bool IsModified(art::Thread* self) const REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + bool modified; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_ diff --git a/runtime/openjdkjvmti/ti_jni.cc b/runtime/openjdkjvmti/ti_jni.cc new file mode 100644 index 0000000000..88f0395ba5 --- /dev/null +++ b/runtime/openjdkjvmti/ti_jni.cc @@ -0,0 +1,91 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_jni.h" + +#include "jni.h" + +#include "art_jvmti.h" +#include "base/mutex.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "runtime.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +jvmtiError JNIUtil::SetJNIFunctionTable(jvmtiEnv* env ATTRIBUTE_UNUSED, + const jniNativeInterface* function_table) { + // While we supporting setting null (which will reset the table), the spec says no. + if (function_table == nullptr) { + return ERR(NULL_POINTER); + } + + art::JNIEnvExt::SetTableOverride(function_table); + return ERR(NONE); +} + +jvmtiError JNIUtil::GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) { + if (function_table == nullptr) { + return ERR(NULL_POINTER); + } + + // We use the generic JNIEnvExt::GetFunctionTable instead of querying a specific JNIEnv, as + // this has to work in the start phase. + + // Figure out which table is current. Conservatively assume check-jni is off. + bool check_jni = false; + art::Runtime* runtime = art::Runtime::Current(); + if (runtime != nullptr && runtime->GetJavaVM() != nullptr) { + check_jni = runtime->GetJavaVM()->IsCheckJniEnabled(); + } + + // Get that table. + const JNINativeInterface* current_table; + { + art::MutexLock mu(art::Thread::Current(), *art::Locks::jni_function_table_lock_); + current_table = art::JNIEnvExt::GetFunctionTable(check_jni); + } + + // Allocate memory and copy the table. + unsigned char* data; + jvmtiError data_result = env->Allocate(sizeof(JNINativeInterface), &data); + if (data_result != ERR(NONE)) { + return data_result; + } + memcpy(data, current_table, sizeof(JNINativeInterface)); + + *function_table = reinterpret_cast<JNINativeInterface*>(data); + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_jni.h b/runtime/openjdkjvmti/ti_jni.h new file mode 100644 index 0000000000..906aab0667 --- /dev/null +++ b/runtime/openjdkjvmti/ti_jni.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +// Note: Currently, JNI function table changes are sensitive to the order of operations wrt/ +// CheckJNI. If an agent sets the function table, and a program than late-enables CheckJNI, +// CheckJNI will not be working (as the agent will forward to the non-CheckJNI table). +// +// This behavior results from our usage of the function table to avoid a check of the +// CheckJNI flag. A future implementation may install on loading of this plugin an +// intermediate function table that explicitly checks the flag, so that switching CheckJNI +// is transparently handled. + +class JNIUtil { + public: + static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table); + + static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_ diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc new file mode 100644 index 0000000000..85d6b729c5 --- /dev/null +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -0,0 +1,129 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_phase.h" + +#include "art_jvmti.h" +#include "base/macros.h" +#include "events-inl.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" + +namespace openjdkjvmti { + +jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0); + +struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { + inline static JNIEnv* GetJniEnv() { + return reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv()); + } + + inline static jthread GetCurrentJThread() { + art::ScopedObjectAccess soa(art::Thread::Current()); + return soa.AddLocalReference<jthread>(soa.Self()->GetPeer()); + } + + void NextRuntimePhase(RuntimePhase phase) OVERRIDE { + // TODO: Events. + switch (phase) { + case RuntimePhase::kInitialAgents: + PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; + break; + case RuntimePhase::kStart: + event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv()); + PhaseUtil::current_phase_ = JVMTI_PHASE_START; + break; + case RuntimePhase::kInit: + { + ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread()); + event_handler->DispatchEvent(nullptr, + ArtJvmtiEvent::kVmInit, + GetJniEnv(), + thread.get()); + PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; + } + break; + case RuntimePhase::kDeath: + event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv()); + PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; + // TODO: Block events now. + break; + } + } + + EventHandler* event_handler = nullptr; +}; + +PhaseUtil::PhaseCallback gPhaseCallback; + +jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase_ptr) { + if (phase_ptr == nullptr) { + return ERR(NULL_POINTER); + } + jvmtiPhase now = PhaseUtil::current_phase_; + DCHECK(now == JVMTI_PHASE_ONLOAD || + now == JVMTI_PHASE_PRIMORDIAL || + now == JVMTI_PHASE_START || + now == JVMTI_PHASE_LIVE || + now == JVMTI_PHASE_DEAD); + *phase_ptr = now; + return ERR(NONE); +} + +void PhaseUtil::SetToOnLoad() { + DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; +} + +void PhaseUtil::SetToPrimordial() { + DCHECK_EQ(static_cast<size_t>(JVMTI_PHASE_ONLOAD), static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD; +} + +void PhaseUtil::SetToLive() { + DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_)); + PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; +} + +void PhaseUtil::Register(EventHandler* handler) { + gPhaseCallback.event_handler = handler; + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add phase callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback); +} + + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h new file mode 100644 index 0000000000..054652a48d --- /dev/null +++ b/runtime/openjdkjvmti/ti_phase.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class EventHandler; + +class PhaseUtil { + public: + static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr); + + static void Register(EventHandler* event_handler); + + // Move the phase from unitialized to LOAD. + static void SetToOnLoad(); + + // Move the phase from LOAD to PRIMORDIAL. + static void SetToPrimordial(); + + // Move the phase from unitialized to LIVE. + static void SetToLive(); + + struct PhaseCallback; + + private: + static jvmtiPhase current_phase_; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_ diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 5bf844564c..34efc502e1 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -36,6 +36,7 @@ #include "android-base/stringprintf.h" #include "art_jvmti.h" +#include "base/array_slice.h" #include "base/logging.h" #include "dex_file.h" #include "dex_file_types.h" @@ -53,6 +54,7 @@ #include "object_lock.h" #include "runtime.h" #include "ScopedLocalRef.h" +#include "transform.h" namespace openjdkjvmti { @@ -207,7 +209,7 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> // Moves dex data to an anonymous, read-only mmap'd region. std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location, jint data_len, - unsigned char* dex_data, + const unsigned char* dex_data, std::string* error_msg) { std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous( StringPrintf("%s-transformed", original_location.c_str()).c_str(), @@ -227,34 +229,140 @@ std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& orig return map; } -// TODO This should handle doing multiple classes at once so we need to do less cleanup when things -// go wrong. -jvmtiError Redefiner::RedefineClass(ArtJvmTiEnv* env, - art::Runtime* runtime, - art::Thread* self, - jclass klass, - const std::string& original_dex_location, - jint data_len, - unsigned char* dex_data, - std::string* error_msg) { - std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location, - data_len, - dex_data, - error_msg)); - std::ostringstream os; +Redefiner::ClassRedefinition::ClassRedefinition( + Redefiner* driver, + jclass klass, + const art::DexFile* redefined_dex_file, + const char* class_sig, + art::ArraySlice<const unsigned char> orig_dex_file) : + driver_(driver), + klass_(klass), + dex_file_(redefined_dex_file), + class_sig_(class_sig), + original_dex_file_(orig_dex_file) { + GetMirrorClass()->MonitorEnter(driver_->self_); +} + +Redefiner::ClassRedefinition::~ClassRedefinition() { + if (driver_ != nullptr) { + GetMirrorClass()->MonitorExit(driver_->self_); + } +} + +jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + jint class_count, + const jvmtiClassDefinition* definitions, + /*out*/std::string* error_msg) { + if (env == nullptr) { + *error_msg = "env was null!"; + return ERR(INVALID_ENVIRONMENT); + } else if (class_count < 0) { + *error_msg = "class_count was less then 0"; + return ERR(ILLEGAL_ARGUMENT); + } else if (class_count == 0) { + // We don't actually need to do anything. Just return OK. + return OK; + } else if (definitions == nullptr) { + *error_msg = "null definitions!"; + return ERR(NULL_POINTER); + } + std::vector<ArtClassDefinition> def_vector; + def_vector.reserve(class_count); + for (jint i = 0; i < class_count; i++) { + // We make a copy of the class_bytes to pass into the retransformation. + // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we + // will need to keep the original bytes around unaltered for subsequent RetransformClasses calls + // to get the passed in bytes. + // TODO Implement saving the original bytes. + unsigned char* class_bytes_copy = nullptr; + jvmtiError res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy); + if (res != OK) { + return res; + } + memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count); + + ArtClassDefinition def; + def.dex_len = definitions[i].class_byte_count; + def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy); + // We are definitely modified. + def.SetModified(); + def.original_dex_file = art::ArraySlice<const unsigned char>(definitions[i].class_bytes, + definitions[i].class_byte_count); + res = Transformer::FillInTransformationData(env, definitions[i].klass, &def); + if (res != OK) { + return res; + } + def_vector.push_back(std::move(def)); + } + // Call all the transformation events. + jvmtiError res = Transformer::RetransformClassesDirect(env, + self, + &def_vector); + if (res != OK) { + // Something went wrong with transformation! + return res; + } + return RedefineClassesDirect(env, runtime, self, def_vector, error_msg); +} + +jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + const std::vector<ArtClassDefinition>& definitions, + std::string* error_msg) { + DCHECK(env != nullptr); + if (definitions.size() == 0) { + // We don't actually need to do anything. Just return OK. + return OK; + } + // Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we + // are going to redefine. + art::jit::ScopedJitSuspend suspend_jit; + // Get shared mutator lock so we can lock all the classes. + art::ScopedObjectAccess soa(self); + Redefiner r(runtime, self, error_msg); + for (const ArtClassDefinition& def : definitions) { + // Only try to transform classes that have been modified. + if (def.IsModified(self)) { + jvmtiError res = r.AddRedefinition(env, def); + if (res != OK) { + return res; + } + } + } + return r.Run(); +} + +jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) { + std::string original_dex_location; + jvmtiError ret = OK; + if ((ret = GetClassLocation(env, def.klass, &original_dex_location))) { + *error_msg_ = "Unable to get original dex file location!"; + return ret; + } char* generic_ptr_unused = nullptr; char* signature_ptr = nullptr; - if (env->GetClassSignature(klass, &signature_ptr, &generic_ptr_unused) != OK) { - signature_ptr = const_cast<char*>("<UNKNOWN CLASS>"); + if ((ret = env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused)) != OK) { + *error_msg_ = "Unable to get class signature!"; + return ret; } + JvmtiUniquePtr generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused)); + JvmtiUniquePtr signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr)); + std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location, + def.dex_len, + def.dex_data.get(), + error_msg_)); + std::ostringstream os; if (map.get() == nullptr) { - os << "Failed to create anonymous mmap for modified dex file of class " << signature_ptr - << "in dex file " << original_dex_location << " because: " << *error_msg; - *error_msg = os.str(); + os << "Failed to create anonymous mmap for modified dex file of class " << def.name + << "in dex file " << original_dex_location << " because: " << *error_msg_; + *error_msg_ = os.str(); return ERR(OUT_OF_MEMORY); } if (map->Size() < sizeof(art::DexFile::Header)) { - *error_msg = "Could not read dex file header because dex_data was too short"; + *error_msg_ = "Could not read dex file header because dex_data was too short"; return ERR(INVALID_CLASS_FORMAT); } uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_; @@ -263,28 +371,25 @@ jvmtiError Redefiner::RedefineClass(ArtJvmTiEnv* env, std::move(map), /*verify*/true, /*verify_checksum*/true, - error_msg)); + error_msg_)); if (dex_file.get() == nullptr) { - os << "Unable to load modified dex file for " << signature_ptr << ": " << *error_msg; - *error_msg = os.str(); + os << "Unable to load modified dex file for " << def.name << ": " << *error_msg_; + *error_msg_ = os.str(); return ERR(INVALID_CLASS_FORMAT); } - // Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we - // are going to redefine. - art::jit::ScopedJitSuspend suspend_jit; - // Get shared mutator lock. - art::ScopedObjectAccess soa(self); - art::StackHandleScope<1> hs(self); - Redefiner r(runtime, self, klass, signature_ptr, dex_file, error_msg); - // Lock around this class to avoid races. - art::ObjectLock<art::mirror::Class> lock(self, hs.NewHandle(r.GetMirrorClass())); - return r.Run(); + redefinitions_.push_back( + Redefiner::ClassRedefinition(this, + def.klass, + dex_file.release(), + signature_ptr, + def.original_dex_file)); + return OK; } // TODO *MAJOR* This should return the actual source java.lang.DexFile object for the klass. // TODO Make mirror of DexFile and associated types to make this less hellish. // TODO Make mirror of BaseDexClassLoader and associated types to make this less hellish. -art::mirror::Object* Redefiner::FindSourceDexFileObject( +art::mirror::Object* Redefiner::ClassRedefinition::FindSourceDexFileObject( art::Handle<art::mirror::ClassLoader> loader) { const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;"; const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;"; @@ -292,14 +397,14 @@ art::mirror::Object* Redefiner::FindSourceDexFileObject( const char* dex_path_list_name = "Ldalvik/system/DexPathList;"; const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;"; - CHECK(!self_->IsExceptionPending()); - art::StackHandleScope<11> hs(self_); - art::ClassLinker* class_linker = runtime_->GetClassLinker(); + CHECK(!driver_->self_->IsExceptionPending()); + art::StackHandleScope<11> hs(driver_->self_); + art::ClassLinker* class_linker = driver_->runtime_->GetClassLinker(); art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>( nullptr)); art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass( - self_, dex_class_loader_name, null_loader))); + driver_->self_, dex_class_loader_name, null_loader))); // Get all the ArtFields so we can look in the BaseDexClassLoader art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField( @@ -307,12 +412,12 @@ art::mirror::Object* Redefiner::FindSourceDexFileObject( CHECK(path_list_field != nullptr); art::ArtField* dex_path_list_element_field = - class_linker->FindClass(self_, dex_path_list_name, null_loader) + class_linker->FindClass(driver_->self_, dex_path_list_name, null_loader) ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name); CHECK(dex_path_list_element_field != nullptr); art::ArtField* element_dex_file_field = - class_linker->FindClass(self_, dex_path_list_element_name, null_loader) + class_linker->FindClass(driver_->self_, dex_path_list_element_name, null_loader) ->FindDeclaredInstanceField("dexFile", dex_file_name); CHECK(element_dex_file_field != nullptr); @@ -327,11 +432,11 @@ art::mirror::Object* Redefiner::FindSourceDexFileObject( art::Handle<art::mirror::Object> path_list( hs.NewHandle(path_list_field->GetObject(loader.Get()))); CHECK(path_list.Get() != nullptr); - CHECK(!self_->IsExceptionPending()); + CHECK(!driver_->self_->IsExceptionPending()); art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( dex_path_list_element_field->GetObject(path_list.Get())-> AsObjectArray<art::mirror::Object>())); - CHECK(!self_->IsExceptionPending()); + CHECK(!driver_->self_->IsExceptionPending()); CHECK(dex_elements_list.Get() != nullptr); size_t num_elements = dex_elements_list->GetLength(); art::MutableHandle<art::mirror::Object> current_element( @@ -343,9 +448,9 @@ art::mirror::Object* Redefiner::FindSourceDexFileObject( for (size_t i = 0; i < num_elements; i++) { current_element.Assign(dex_elements_list->Get(i)); CHECK(current_element.Get() != nullptr); - CHECK(!self_->IsExceptionPending()); + CHECK(!driver_->self_->IsExceptionPending()); CHECK(dex_elements_list.Get() != nullptr); - CHECK_EQ(current_element->GetClass(), class_linker->FindClass(self_, + CHECK_EQ(current_element->GetClass(), class_linker->FindClass(driver_->self_, dex_path_list_element_name, null_loader)); // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class @@ -360,22 +465,23 @@ art::mirror::Object* Redefiner::FindSourceDexFileObject( return nullptr; } -art::mirror::Class* Redefiner::GetMirrorClass() { - return self_->DecodeJObject(klass_)->AsClass(); +art::mirror::Class* Redefiner::ClassRedefinition::GetMirrorClass() { + return driver_->self_->DecodeJObject(klass_)->AsClass(); } -art::mirror::ClassLoader* Redefiner::GetClassLoader() { +art::mirror::ClassLoader* Redefiner::ClassRedefinition::GetClassLoader() { return GetMirrorClass()->GetClassLoader(); } -art::mirror::DexCache* Redefiner::CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader) { - return runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()); +art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache( + art::Handle<art::mirror::ClassLoader> loader) { + return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()); } // TODO Really wishing I had that mirror of java.lang.DexFile now. -art::mirror::LongArray* Redefiner::AllocateDexFileCookie( +art::mirror::LongArray* Redefiner::ClassRedefinition::AllocateDexFileCookie( art::Handle<art::mirror::Object> java_dex_file_obj) { - art::StackHandleScope<2> hs(self_); + art::StackHandleScope<2> hs(driver_->self_); // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until // the object is finalized. Since they always point to the same array if mCookie is not null we // just use the mInternalCookie field. We will update one or both of these fields later. @@ -390,9 +496,9 @@ art::mirror::LongArray* Redefiner::AllocateDexFileCookie( CHECK(cookie.Get() != nullptr); CHECK_GE(cookie->GetLength(), 1); art::Handle<art::mirror::LongArray> new_cookie( - hs.NewHandle(art::mirror::LongArray::Alloc(self_, cookie->GetLength() + 1))); + hs.NewHandle(art::mirror::LongArray::Alloc(driver_->self_, cookie->GetLength() + 1))); if (new_cookie.Get() == nullptr) { - self_->AssertPendingOOMException(); + driver_->self_->AssertPendingOOMException(); return nullptr; } // Copy the oat-dex field at the start. @@ -405,61 +511,65 @@ art::mirror::LongArray* Redefiner::AllocateDexFileCookie( return new_cookie.Get(); } -void Redefiner::RecordFailure(jvmtiError result, const std::string& error_msg) { +void Redefiner::RecordFailure(jvmtiError result, + const std::string& class_sig, + const std::string& error_msg) { *error_msg_ = StringPrintf("Unable to perform redefinition of '%s': %s", - class_sig_, + class_sig.c_str(), error_msg.c_str()); result_ = result; } -bool Redefiner::FinishRemainingAllocations( - /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader, - /*out*/art::MutableHandle<art::mirror::Object>* java_dex_file_obj, - /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie, - /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) { - art::StackHandleScope<4> hs(self_); - // This shouldn't allocate - art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); - if (loader.Get() == nullptr) { - // TODO Better error msg. - RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); - return false; +// 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); } - art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader))); - if (dex_file_obj.Get() == nullptr) { - // TODO Better error msg. - RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); - return false; + 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()); } - art::Handle<art::mirror::LongArray> new_cookie(hs.NewHandle(AllocateDexFileCookie(dex_file_obj))); - if (new_cookie.Get() == nullptr) { - self_->AssertPendingOOMException(); - self_->ClearException(); - RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader"); - return false; + + // See if we already have one set. + art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData()); + if (!ext.IsNull()) { + art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes()); + if (!old_original_bytes.IsNull()) { + // We do. Use it. + return old_original_bytes.Ptr(); + } } - art::Handle<art::mirror::DexCache> dex_cache(hs.NewHandle(CreateNewDexCache(loader))); - if (dex_cache.Get() == nullptr) { - self_->AssertPendingOOMException(); - self_->ClearException(); - RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); - return false; + + // Copy the current dex_file + const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile(); + // TODO Handle this or make it so it cannot happen. + if (current_dex_file.NumClassDefs() != 1) { + 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!"; } - source_class_loader->Assign(loader.Get()); - java_dex_file_obj->Assign(dex_file_obj.Get()); - new_dex_file_cookie->Assign(new_cookie.Get()); - new_dex_cache->Assign(dex_cache.Get()); - return true; + return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size()); } struct CallbackCtx { - Redefiner* const r; art::LinearAlloc* allocator; std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map; std::unordered_set<art::ArtMethod*> obsolete_methods; - CallbackCtx(Redefiner* self, art::LinearAlloc* alloc) - : r(self), allocator(alloc) {} + explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {} }; void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS { @@ -472,11 +582,12 @@ void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SA // This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is // updated so they will be run. -void Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) { +// TODO Rewrite so we can do this only once regardless of how many redefinitions there are. +void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) { art::ScopedAssertNoThreadSuspension ns("No thread suspension during thread stack walking"); art::mirror::ClassExt* ext = art_klass->GetExtData(); CHECK(ext->GetObsoleteMethods() != nullptr); - CallbackCtx ctx(this, art_klass->GetClassLoader()->GetAllocator()); + CallbackCtx ctx(art_klass->GetClassLoader()->GetAllocator()); // Add all the declared methods to the map for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) { ctx.obsolete_methods.insert(&m); @@ -484,7 +595,7 @@ void Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) { DCHECK(!m.IsIntrinsic()); } { - art::MutexLock mu(self_, *art::Locks::thread_list_lock_); + art::MutexLock mu(driver_->self_, *art::Locks::thread_list_lock_); art::ThreadList* list = art::Runtime::Current()->GetThreadList(); list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx)); } @@ -493,7 +604,7 @@ void Redefiner::FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) { // Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to // figure out their DexCaches. -void Redefiner::FillObsoleteMethodMap( +void Redefiner::ClassRedefinition::FillObsoleteMethodMap( art::mirror::Class* art_klass, const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) { int32_t index = 0; @@ -532,9 +643,9 @@ void Redefiner::EnsureObsoleteMethodsAreDeoptimized() { i->ReJitEverything("libOpenJkdJvmti - Class Redefinition"); } -bool Redefiner::CheckClass() { +bool Redefiner::ClassRedefinition::CheckClass() { // TODO Might just want to put it in a ObjPtr and NoSuspend assert. - art::StackHandleScope<1> hs(self_); + art::StackHandleScope<1> hs(driver_->self_); // Easy check that only 1 class def is present. if (dex_file_->NumClassDefs() != 1) { RecordFailure(ERR(ILLEGAL_ARGUMENT), @@ -607,14 +718,15 @@ bool Redefiner::CheckClass() { } } LOG(WARNING) << "No verification is done on annotations of redefined classes."; + LOG(WARNING) << "Bytecodes of redefinitions are not verified."; return true; } // TODO Move this to use IsRedefinable when that function is made. -bool Redefiner::CheckRedefinable() { +bool Redefiner::ClassRedefinition::CheckRedefinable() { std::string err; - art::StackHandleScope<1> hs(self_); + art::StackHandleScope<1> hs(driver_->self_); art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass())); jvmtiError res = Redefiner::GetClassRedefinitionError(h_klass, &err); @@ -626,46 +738,233 @@ bool Redefiner::CheckRedefinable() { } } -bool Redefiner::CheckRedefinitionIsValid() { +bool Redefiner::ClassRedefinition::CheckRedefinitionIsValid() { return CheckRedefinable() && CheckClass() && CheckSameFields() && CheckSameMethods(); } +// A wrapper that lets us hold onto the arbitrary sized data needed for redefinitions in a +// reasonably sane way. This adds no fields to the normal ObjectArray. By doing this we can avoid +// having to deal with the fact that we need to hold an arbitrary number of references live. +class RedefinitionDataHolder { + public: + enum DataSlot : int32_t { + kSlotSourceClassLoader = 0, + kSlotJavaDexFile = 1, + kSlotNewDexFileCookie = 2, + kSlotNewDexCache = 3, + kSlotMirrorClass = 4, + kSlotOrigDexFile = 5, + + // Must be last one. + kNumSlots = 6, + }; + + // This needs to have a HandleScope passed in that is capable of creating a new Handle without + // overflowing. Only one handle will be created. This object has a lifetime identical to that of + // the passed in handle-scope. + RedefinitionDataHolder(art::StackHandleScope<1>* hs, + art::Runtime* runtime, + art::Thread* self, + int32_t num_redefinitions) REQUIRES_SHARED(art::Locks::mutator_lock_) : + arr_( + hs->NewHandle( + art::mirror::ObjectArray<art::mirror::Object>::Alloc( + self, + runtime->GetClassLinker()->GetClassRoot(art::ClassLinker::kObjectArrayClass), + num_redefinitions * kNumSlots))) {} + + bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) { + return arr_.IsNull(); + } + + // TODO Maybe make an iterable view type to simplify using this. + art::mirror::ClassLoader* GetSourceClassLoader(jint klass_index) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::ClassLoader*>(GetSlot(klass_index, kSlotSourceClassLoader)); + } + art::mirror::Object* GetJavaDexFile(jint klass_index) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return GetSlot(klass_index, kSlotJavaDexFile); + } + art::mirror::LongArray* GetNewDexFileCookie(jint klass_index) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::LongArray*>(GetSlot(klass_index, kSlotNewDexFileCookie)); + } + art::mirror::DexCache* GetNewDexCache(jint klass_index) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::DexCache*>(GetSlot(klass_index, kSlotNewDexCache)); + } + art::mirror::Class* GetMirrorClass(jint klass_index) REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass)); + } + + art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile)); + } + + void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotSourceClassLoader, loader); + } + void SetJavaDexFile(jint klass_index, art::mirror::Object* dexfile) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotJavaDexFile, dexfile); + } + void SetNewDexFileCookie(jint klass_index, art::mirror::LongArray* cookie) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotNewDexFileCookie, cookie); + } + void SetNewDexCache(jint klass_index, art::mirror::DexCache* cache) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotNewDexCache, cache); + } + void SetMirrorClass(jint klass_index, art::mirror::Class* klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotMirrorClass, klass); + } + void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotOrigDexFile, bytes); + } + + int32_t Length() REQUIRES_SHARED(art::Locks::mutator_lock_) { + return arr_->GetLength() / kNumSlots; + } + + private: + art::Handle<art::mirror::ObjectArray<art::mirror::Object>> arr_; + + art::mirror::Object* GetSlot(jint klass_index, + DataSlot slot) REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK_LT(klass_index, Length()); + return arr_->Get((kNumSlots * klass_index) + slot); + } + + void SetSlot(jint klass_index, + DataSlot slot, + art::ObjPtr<art::mirror::Object> obj) REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(!art::Runtime::Current()->IsActiveTransaction()); + DCHECK_LT(klass_index, Length()); + arr_->Set<false>((kNumSlots * klass_index) + slot, obj); + } + + DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder); +}; + +bool Redefiner::ClassRedefinition::FinishRemainingAllocations( + int32_t klass_index, /*out*/RedefinitionDataHolder* holder) { + art::StackHandleScope<2> hs(driver_->self_); + holder->SetMirrorClass(klass_index, GetMirrorClass()); + // This shouldn't allocate + art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); + holder->SetSourceClassLoader(klass_index, loader.Get()); + if (loader.Get() == nullptr) { + // TODO Better error msg. + RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); + return false; + } + art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader))); + holder->SetJavaDexFile(klass_index, dex_file_obj.Get()); + if (dex_file_obj.Get() == nullptr) { + // TODO Better error msg. + RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); + return false; + } + holder->SetNewDexFileCookie(klass_index, AllocateDexFileCookie(dex_file_obj)); + if (holder->GetNewDexFileCookie(klass_index) == nullptr) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader"); + return false; + } + holder->SetNewDexCache(klass_index, CreateNewDexCache(loader)); + if (holder->GetNewDexCache(klass_index) == nullptr) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); + return false; + } + + // We won't always need to set this field. + holder->SetOriginalDexFileBytes(klass_index, AllocateOrGetOriginalDexFileBytes()); + if (holder->GetOriginalDexFileBytes(klass_index) == nullptr) { + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file"); + return false; + } + return true; +} + +bool Redefiner::CheckAllRedefinitionAreValid() { + for (Redefiner::ClassRedefinition& redef : redefinitions_) { + if (!redef.CheckRedefinitionIsValid()) { + return false; + } + } + return true; +} + +bool Redefiner::EnsureAllClassAllocationsFinished() { + for (Redefiner::ClassRedefinition& redef : redefinitions_) { + if (!redef.EnsureClassAllocationsFinished()) { + return false; + } + } + return true; +} + +bool Redefiner::FinishAllRemainingAllocations(RedefinitionDataHolder& holder) { + int32_t cnt = 0; + for (Redefiner::ClassRedefinition& redef : redefinitions_) { + // Allocate the data this redefinition requires. + if (!redef.FinishRemainingAllocations(cnt, &holder)) { + return false; + } + cnt++; + } + return true; +} + +void Redefiner::ClassRedefinition::ReleaseDexFile() { + dex_file_.release(); +} + +void Redefiner::ReleaseAllDexFiles() { + for (Redefiner::ClassRedefinition& redef : redefinitions_) { + redef.ReleaseDexFile(); + } +} + jvmtiError Redefiner::Run() { - art::StackHandleScope<5> hs(self_); - // TODO We might want to have a global lock (or one based on the class being redefined at least) - // in order to make cleanup easier. Not a huge deal though. - // + art::StackHandleScope<1> hs(self_); + // Allocate an array to hold onto all java temporary objects associated with this redefinition. + // We will let this be collected after the end of this function. + RedefinitionDataHolder holder(&hs, runtime_, self_, redefinitions_.size()); + if (holder.IsNull()) { + self_->AssertPendingOOMException(); + self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate storage for temporaries"); + return result_; + } + // First we just allocate the ClassExt and its fields that we need. These can be updated // atomically without any issues (since we allocate the map arrays as empty) so we don't bother // doing a try loop. The other allocations we need to ensure that nothing has changed in the time // between allocating them and pausing all threads before we can update them so we need to do a // try loop. - if (!CheckRedefinitionIsValid() || !EnsureClassAllocationsFinished()) { - return result_; - } - art::MutableHandle<art::mirror::ClassLoader> source_class_loader( - hs.NewHandle<art::mirror::ClassLoader>(nullptr)); - art::MutableHandle<art::mirror::Object> java_dex_file( - hs.NewHandle<art::mirror::Object>(nullptr)); - art::MutableHandle<art::mirror::LongArray> new_dex_file_cookie( - hs.NewHandle<art::mirror::LongArray>(nullptr)); - art::MutableHandle<art::mirror::DexCache> new_dex_cache( - hs.NewHandle<art::mirror::DexCache>(nullptr)); - if (!FinishRemainingAllocations(&source_class_loader, - &java_dex_file, - &new_dex_file_cookie, - &new_dex_cache)) { + if (!CheckAllRedefinitionAreValid() || + !EnsureAllClassAllocationsFinished() || + !FinishAllRemainingAllocations(holder)) { // TODO Null out the ClassExt fields we allocated (if possible, might be racing with another // redefineclass call which made it even bigger. Leak shouldn't be huge (2x array of size - // declared_methods_.length) but would be good to get rid of. - // new_dex_file_cookie & new_dex_cache should be cleaned up by the GC. + // declared_methods_.length) but would be good to get rid of. All other allocations should be + // cleaned up by the GC eventually. return result_; } - // Get the mirror class now that we aren't allocating anymore. - art::Handle<art::mirror::Class> art_class(hs.NewHandle(GetMirrorClass())); // Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done // allocating so no deadlocks. art::gc::Heap* heap = runtime_->GetHeap(); @@ -673,31 +972,29 @@ jvmtiError Redefiner::Run() { // GC moving objects can cause deadlocks as we are deoptimizing the stack. heap->IncrementDisableMovingGC(self_); } - // Enable assertion that this thread isn't interrupted during this installation. - // After this we will need to do real cleanup in case of failure. Prior to this we could simply - // return and would let everything get cleaned up or harmlessly leaked. // Do transition to final suspension // TODO We might want to give this its own suspended state! // TODO This isn't right. We need to change state without any chance of suspend ideally! self_->TransitionFromRunnableToSuspended(art::ThreadState::kNative); runtime_->GetThreadList()->SuspendAll( - "Final installation of redefined Class!", /*long_suspend*/true); + "Final installation of redefined Classes!", /*long_suspend*/true); // TODO We need to invalidate all breakpoints in the redefined class with the debugger. // TODO We need to deal with any instrumentation/debugger deoptimized_methods_. // TODO We need to update all debugger MethodIDs so they note the method they point to is // obsolete or implement some other well defined semantics. // TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods. - // TODO Might want to move this into a different type. - // Now we reach the part where we must do active cleanup if something fails. - // TODO We should really Retry if this fails instead of simply aborting. - // Set the new DexFileCookie returns the original so we can fix it back up if redefinition fails - art::ObjPtr<art::mirror::LongArray> original_dex_file_cookie(nullptr); - UpdateJavaDexFile(java_dex_file.Get(), new_dex_file_cookie.Get(), &original_dex_file_cookie); - FindAndAllocateObsoleteMethods(art_class.Get()); - UpdateClass(art_class.Get(), new_dex_cache.Get()); + int32_t cnt = 0; + for (Redefiner::ClassRedefinition& redef : redefinitions_) { + art::mirror::Class* klass = holder.GetMirrorClass(cnt); + redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt)); + // TODO Rewrite so we don't do a stack walk for each and every class. + redef.FindAndAllocateObsoleteMethods(klass); + redef.UpdateClass(klass, holder.GetNewDexCache(cnt), holder.GetOriginalDexFileBytes(cnt)); + cnt++; + } // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have // pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the - // DexCache. + // DexCache. (b/33630159) // TODO This can fail (leave some methods optimized) near runtime methods (including // quick-to-interpreter transition function). // TODO We probably don't need this at all once we have a way to ensure that the @@ -705,26 +1002,25 @@ jvmtiError Redefiner::Run() { // stack-walker. EnsureObsoleteMethodsAreDeoptimized(); // TODO Verify the new Class. - // TODO Failure then undo updates to class // TODO Shrink the obsolete method maps if possible? // TODO find appropriate class loader. // TODO Put this into a scoped thing. runtime_->GetThreadList()->ResumeAll(); // Get back shared mutator lock as expected for return. self_->TransitionFromSuspendedToRunnable(); - // TODO Do the dex_file_ release at a more reasonable place. This works but it muddles who really - // owns the DexFile. - dex_file_.release(); + // TODO Do the dex_file release at a more reasonable place. This works but it muddles who really + // owns the DexFile and when ownership is transferred. + ReleaseAllDexFiles(); if (heap->IsGcConcurrentAndMoving()) { heap->DecrementDisableMovingGC(self_); } return OK; } -void Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache, - const art::DexFile::ClassDef& class_def) { - art::ClassLinker* linker = runtime_->GetClassLinker(); +void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + const art::DexFile::ClassDef& class_def) { + art::ClassLinker* linker = driver_->runtime_->GetClassLinker(); art::PointerSize image_pointer_size = linker->GetImagePointerSize(); const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_); const art::DexFile& old_dex_file = mclass->GetDexFile(); @@ -757,16 +1053,15 @@ void Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, linker->SetEntryPointsToInterpreter(&method); method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx)); method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size); - method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size); // Notify the jit that this method is redefined. - art::jit::Jit* jit = runtime_->GetJit(); + art::jit::Jit* jit = driver_->runtime_->GetJit(); if (jit != nullptr) { jit->GetCodeCache()->NotifyMethodRedefined(&method); } } } -void Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) { +void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) { // TODO The IFields & SFields pointers should be combined like the methods_ arrays were. for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) { for (art::ArtField& field : fields_iter) { @@ -787,25 +1082,29 @@ void Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) { } // Performs updates to class that will allow us to verify it. -void Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache) { - const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef( - *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_)); - DCHECK(class_def != nullptr); - UpdateMethods(mclass, new_dex_cache, *class_def); +void Redefiner::ClassRedefinition::UpdateClass( + art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + art::ObjPtr<art::mirror::ByteArray> original_dex_file) { + DCHECK_EQ(dex_file_->NumClassDefs(), 1u); + const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0); + UpdateMethods(mclass, new_dex_cache, class_def); UpdateFields(mclass); // Update the class fields. // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed // to call GetReturnTypeDescriptor and GetParameterTypeList above). mclass->SetDexCache(new_dex_cache.Ptr()); - mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def)); - mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_))); + mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(class_def)); + mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str()))); + art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData()); + CHECK(!ext.IsNull()); + ext->SetOriginalDexFileBytes(original_dex_file); } -void Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, - art::ObjPtr<art::mirror::LongArray> new_cookie, - /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie) { +void Redefiner::ClassRedefinition::UpdateJavaDexFile( + art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> new_cookie) { art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( "mInternalCookie", "Ljava/lang/Object;"); art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( @@ -816,7 +1115,6 @@ void Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file art::ObjPtr<art::mirror::LongArray> orig_cookie( cookie_field->GetObject(java_dex_file)->AsLongArray()); internal_cookie_field->SetObject<false>(java_dex_file, new_cookie); - *original_cookie = orig_internal_cookie; if (!orig_cookie.IsNull()) { cookie_field->SetObject<false>(java_dex_file, new_cookie); } @@ -824,21 +1122,22 @@ void Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file // This function does all (java) allocations we need to do for the Class being redefined. // TODO Change this name maybe? -bool Redefiner::EnsureClassAllocationsFinished() { - art::StackHandleScope<2> hs(self_); - art::Handle<art::mirror::Class> klass(hs.NewHandle(self_->DecodeJObject(klass_)->AsClass())); +bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() { + art::StackHandleScope<2> hs(driver_->self_); + art::Handle<art::mirror::Class> klass(hs.NewHandle( + driver_->self_->DecodeJObject(klass_)->AsClass())); if (klass.Get() == nullptr) { RecordFailure(ERR(INVALID_CLASS), "Unable to decode class argument!"); return false; } // Allocate the classExt - art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(self_))); + art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(driver_->self_))); if (ext.Get() == nullptr) { // No memory. Clear exception (it's not useful) and return error. // TODO This doesn't need to be fatal. We could just not support obsolete methods after hitting // this case. - self_->AssertPendingOOMException(); - self_->ClearException(); + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate ClassExt"); return false; } @@ -849,12 +1148,12 @@ bool Redefiner::EnsureClassAllocationsFinished() { // TODO Clear these after we walk the stacks in order to free them in the (likely?) event there // are no obsolete methods. { - art::ObjectLock<art::mirror::ClassExt> lock(self_, ext); + art::ObjectLock<art::mirror::ClassExt> lock(driver_->self_, ext); if (!ext->ExtendObsoleteArrays( - self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) { + driver_->self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) { // OOM. Clear exception and return error. - self_->AssertPendingOOMException(); - self_->ClearException(); + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate/extend obsolete methods map"); return false; } diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 5852309291..29a7e1f3ac 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -38,6 +38,7 @@ #include "art_jvmti.h" #include "art_method.h" +#include "base/array_slice.h" #include "class_linker.h" #include "dex_file.h" #include "gc_root-inl.h" @@ -56,6 +57,7 @@ #include "obj_ptr.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" +#include "ti_class_definition.h" #include "thread_list.h" #include "transform.h" #include "utf.h" @@ -63,50 +65,170 @@ namespace openjdkjvmti { +class RedefinitionDataHolder; + // Class that can redefine a single class's methods. // TODO We should really make this be driven by an outside class so we can do multiple classes at // the same time and have less required cleanup. class Redefiner { public: - // Redefine the given class with the given dex data. Note this function does not take ownership of - // the dex_data pointer. It is not used after this call however and may be freed if desired. + // Redefine the given classes with the given dex data. Note this function does not take ownership + // of the dex_data pointers. It is not used after this call however and may be freed if desired. + // The caller is responsible for freeing it. The runtime makes its own copy of the data. This + // function does not call the transformation events. + // TODO Check modified flag of the definitions. + static jvmtiError RedefineClassesDirect(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + const std::vector<ArtClassDefinition>& definitions, + /*out*/std::string* error_msg); + + // Redefine the given classes with the given dex data. Note this function does not take ownership + // of the dex_data pointers. It is not used after this call however and may be freed if desired. // The caller is responsible for freeing it. The runtime makes its own copy of the data. - static jvmtiError RedefineClass(ArtJvmTiEnv* env, - art::Runtime* runtime, - art::Thread* self, - jclass klass, - const std::string& original_dex_location, - jint data_len, - unsigned char* dex_data, - std::string* error_msg); + // TODO This function should call the transformation events. + static jvmtiError RedefineClasses(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + jint class_count, + const jvmtiClassDefinition* definitions, + /*out*/std::string* error_msg); static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable); private: + class ClassRedefinition { + public: + ClassRedefinition(Redefiner* driver, + jclass klass, + const art::DexFile* redefined_dex_file, + const char* class_sig, + art::ArraySlice<const unsigned char> orig_dex_file) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + // NO_THREAD_SAFETY_ANALYSIS so we can unlock the class in the destructor. + ~ClassRedefinition() NO_THREAD_SAFETY_ANALYSIS; + + // Move constructor so we can put these into a vector. + ClassRedefinition(ClassRedefinition&& other) + : driver_(other.driver_), + klass_(other.klass_), + dex_file_(std::move(other.dex_file_)), + class_sig_(std::move(other.class_sig_)), + original_dex_file_(other.original_dex_file_) { + other.driver_ = nullptr; + } + + art::mirror::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_); + art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath. + // TODO Make sure the DexFile object returned is the one that the klass_ actually comes from. + art::mirror::Object* FindSourceDexFileObject(art::Handle<art::mirror::ClassLoader> loader) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Allocates and fills the new DexFileCookie + art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> j_dex_file_obj) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + // This may return nullptr with a OOME pending if allocation fails. + art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes() + REQUIRES_SHARED(art::Locks::mutator_lock_); + + void RecordFailure(jvmtiError e, const std::string& err) { + driver_->RecordFailure(e, class_sig_, err); + } + + bool FinishRemainingAllocations(int32_t klass_index, /*out*/RedefinitionDataHolder* holder) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) + REQUIRES(art::Locks::mutator_lock_); + + void FillObsoleteMethodMap( + art::mirror::Class* art_klass, + const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) + REQUIRES(art::Locks::mutator_lock_); + + + // Checks that the dex file contains only the single expected class and that the top-level class + // data has not been modified in an incompatible manner. + bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Preallocates all needed allocations in klass so that we can pause execution safely. + // TODO We should be able to free the arrays if they end up not being used. Investigate doing + // this in the future. For now we will just take the memory hit. + bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // This will check that no constraints are violated (more than 1 class in dex file, any changes + // in number/declaration of methods & fields, changes in access flags, etc.) + bool CheckRedefinitionIsValid() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Checks that the class can even be redefined. + bool CheckRedefinable() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Checks that the dex file does not add/remove methods. + bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_) { + LOG(WARNING) << "methods are not checked for modification currently"; + return true; + } + + // Checks that the dex file does not modify fields + bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_) { + LOG(WARNING) << "Fields are not checked for modification currently"; + return true; + } + + void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> new_cookie) + REQUIRES(art::Locks::mutator_lock_); + + void UpdateFields(art::ObjPtr<art::mirror::Class> mclass) + REQUIRES(art::Locks::mutator_lock_); + + void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + const art::DexFile::ClassDef& class_def) + REQUIRES(art::Locks::mutator_lock_); + + void UpdateClass(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + art::ObjPtr<art::mirror::ByteArray> original_dex_file) + REQUIRES(art::Locks::mutator_lock_); + + void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + Redefiner* driver_; + jclass klass_; + std::unique_ptr<const art::DexFile> dex_file_; + std::string class_sig_; + art::ArraySlice<const unsigned char> original_dex_file_; + }; + jvmtiError result_; art::Runtime* runtime_; art::Thread* self_; + std::vector<ClassRedefinition> redefinitions_; // Kept as a jclass since we have weird run-state changes that make keeping it around as a // mirror::Class difficult and confusing. - jclass klass_; - std::unique_ptr<const art::DexFile> dex_file_; std::string* error_msg_; - char* class_sig_; // TODO Maybe change jclass to a mirror::Class Redefiner(art::Runtime* runtime, art::Thread* self, - jclass klass, - char* class_sig, - std::unique_ptr<const art::DexFile>& redefined_dex_file, std::string* error_msg) : result_(ERR(INTERNAL)), runtime_(runtime), self_(self), - klass_(klass), - dex_file_(std::move(redefined_dex_file)), - error_msg_(error_msg), - class_sig_(class_sig) { } + redefinitions_(), + error_msg_(error_msg) { } + + jvmtiError AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) + REQUIRES_SHARED(art::Locks::mutator_lock_); static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass, /*out*/std::string* error_msg) @@ -114,23 +236,17 @@ class Redefiner { static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location, jint data_len, - unsigned char* dex_data, + const unsigned char* dex_data, std::string* error_msg); // TODO Put on all the lock qualifiers. jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_); - bool FinishRemainingAllocations( - /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader, - /*out*/art::MutableHandle<art::mirror::Object>* source_dex_file_obj, - /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie, - /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) + bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_); + bool EnsureAllClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); + bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); - - // Preallocates all needed allocations in klass so that we can pause execution safely. - // TODO We should be able to free the arrays if they end up not being used. Investigate doing this - // in the future. For now we will just take the memory hit. - bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); + void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_); // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have // pointers to their ArtMethods stashed in registers that they then use to attempt to hit the @@ -140,70 +256,12 @@ class Redefiner { REQUIRES(!art::Locks::thread_list_lock_, !art::Locks::classlinker_classes_lock_); - art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_); - - // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath. - // TODO Make sure the DexFile object returned is the one that the klass_ actually comes from. - art::mirror::Object* FindSourceDexFileObject(art::Handle<art::mirror::ClassLoader> loader) - REQUIRES_SHARED(art::Locks::mutator_lock_); - - art::mirror::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_); - - // Allocates and fills the new DexFileCookie - art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> java_dex_file_obj) - REQUIRES_SHARED(art::Locks::mutator_lock_); - - art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader) - REQUIRES_SHARED(art::Locks::mutator_lock_); - - void RecordFailure(jvmtiError result, const std::string& error_msg); - - // This will check that no constraints are violated (more than 1 class in dex file, any changes in - // number/declaration of methods & fields, changes in access flags, etc.) - bool CheckRedefinitionIsValid() REQUIRES_SHARED(art::Locks::mutator_lock_); - - // Checks that the class can even be redefined. - bool CheckRedefinable() REQUIRES_SHARED(art::Locks::mutator_lock_); - - // Checks that the dex file does not add/remove methods. - bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_) { - LOG(WARNING) << "methods are not checked for modification currently"; - return true; + void RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg); + void RecordFailure(jvmtiError result, const std::string& error_msg) { + RecordFailure(result, "NO CLASS", error_msg); } - // Checks that the dex file does not modify fields - bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_) { - LOG(WARNING) << "Fields are not checked for modification currently"; - return true; - } - - // Checks that the dex file contains only the single expected class and that the top-level class - // data has not been modified in an incompatible manner. - bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_); - - void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, - art::ObjPtr<art::mirror::LongArray> new_cookie, - /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie) - REQUIRES(art::Locks::mutator_lock_); - - void UpdateFields(art::ObjPtr<art::mirror::Class> mclass) - REQUIRES(art::Locks::mutator_lock_); - - void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache, - const art::DexFile::ClassDef& class_def) - REQUIRES(art::Locks::mutator_lock_); - - void UpdateClass(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache) - REQUIRES(art::Locks::mutator_lock_); - - void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) - REQUIRES(art::Locks::mutator_lock_); - - void FillObsoleteMethodMap(art::mirror::Class* art_klass, - const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) - REQUIRES(art::Locks::mutator_lock_); + friend struct CallbackCtx; }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc new file mode 100644 index 0000000000..913d2b6a74 --- /dev/null +++ b/runtime/openjdkjvmti/ti_search.cc @@ -0,0 +1,122 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_search.h" + +#include "jni.h" + +#include "art_jvmti.h" +#include "base/macros.h" +#include "class_linker.h" +#include "dex_file.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "ScopedLocalRef.h" + +namespace openjdkjvmti { + +jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED, + const char* segment) { + art::Runtime* current = art::Runtime::Current(); + if (current == nullptr) { + return ERR(WRONG_PHASE); + } + if (current->GetClassLinker() == nullptr) { + // TODO: Support boot classpath change in OnLoad. + return ERR(WRONG_PHASE); + } + if (segment == nullptr) { + return ERR(NULL_POINTER); + } + + std::string error_msg; + std::vector<std::unique_ptr<const art::DexFile>> dex_files; + if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) { + LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg; + return ERR(ILLEGAL_ARGUMENT); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) { + current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release()); + } + + return ERR(NONE); +} + +jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, + const char* segment) { + if (segment == nullptr) { + return ERR(NULL_POINTER); + } + + art::Runtime* current = art::Runtime::Current(); + if (current == nullptr) { + return ERR(WRONG_PHASE); + } + jobject sys_class_loader = current->GetSystemClassLoader(); + if (sys_class_loader == nullptr) { + // TODO: Support classpath change in OnLoad. + return ERR(WRONG_PHASE); + } + + // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside, + // exceptions are swallowed. + + art::Thread* self = art::Thread::Current(); + JNIEnv* env = self->GetJniEnv(); + if (!env->IsInstanceOf(sys_class_loader, + art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) { + return ERR(INTERNAL); + } + + jmethodID add_dex_path_id = env->GetMethodID( + art::WellKnownClasses::dalvik_system_BaseDexClassLoader, + "addDexPath", + "(Ljava/lang/String;)V"); + if (add_dex_path_id == nullptr) { + return ERR(INTERNAL); + } + + ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment)); + if (dex_path.get() == nullptr) { + return ERR(INTERNAL); + } + env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get()); + + if (env->ExceptionCheck()) { + env->ExceptionClear(); + return ERR(ILLEGAL_ARGUMENT); + } + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h new file mode 100644 index 0000000000..6a52e80405 --- /dev/null +++ b/runtime/openjdkjvmti/ti_search.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ + +#include "jvmti.h" + +namespace openjdkjvmti { + +class SearchUtil { + public: + static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment); + + static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_ diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc index 098cedbffa..4cf55a6a98 100644 --- a/runtime/openjdkjvmti/ti_stack.cc +++ b/runtime/openjdkjvmti/ti_stack.cc @@ -31,6 +31,7 @@ #include "ti_stack.h" +#include <algorithm> #include <list> #include <unordered_map> #include <vector> @@ -42,6 +43,7 @@ #include "base/mutex.h" #include "dex_file.h" #include "dex_file_annotations.h" +#include "handle_scope-inl.h" #include "jni_env_ext.h" #include "jni_internal.h" #include "mirror/class.h" @@ -159,24 +161,43 @@ static jvmtiError TranslateFrameVector(const std::vector<jvmtiFrameInfo>& frames return ERR(NONE); } +static jvmtiError GetThread(JNIEnv* env, jthread java_thread, art::Thread** thread) { + if (java_thread == nullptr) { + *thread = art::Thread::Current(); + if (*thread == nullptr) { + // GetStackTrace can only be run during the live phase, so the current thread should be + // attached and thus available. Getting a null for current means we're starting up or + // dying. + return ERR(WRONG_PHASE); + } + } else { + if (!env->IsInstanceOf(java_thread, art::WellKnownClasses::java_lang_Thread)) { + return ERR(INVALID_THREAD); + } + + // TODO: Need non-aborting call here, to return JVMTI_ERROR_INVALID_THREAD. + art::ScopedObjectAccess soa(art::Thread::Current()); + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + *thread = art::Thread::FromManagedThread(soa, java_thread); + if (*thread == nullptr) { + return ERR(THREAD_NOT_ALIVE); + } + } + return ERR(NONE); +} + jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, jthread java_thread, jint start_depth, jint max_frame_count, jvmtiFrameInfo* frame_buffer, jint* count_ptr) { - if (java_thread == nullptr) { - return ERR(INVALID_THREAD); - } - art::Thread* thread; - { - // TODO: Need non-aborting call here, to return JVMTI_ERROR_INVALID_THREAD. - art::ScopedObjectAccess soa(art::Thread::Current()); - art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); - thread = art::Thread::FromManagedThread(soa, java_thread); - DCHECK(thread != nullptr); + jvmtiError thread_error = GetThread(art::Thread::Current()->GetJniEnv(), java_thread, &thread); + if (thread_error != ERR(NONE)) { + return thread_error; } + DCHECK(thread != nullptr); art::ThreadState state = thread->GetState(); if (state == art::ThreadState::kStarting || @@ -374,4 +395,331 @@ jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env, return ERR(NONE); } +jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr) { + if (max_frame_count < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + if (thread_count < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + if (thread_count == 0) { + *stack_info_ptr = nullptr; + return ERR(NONE); + } + if (stack_info_ptr == nullptr || stack_info_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Thread* current = art::Thread::Current(); + art::ScopedObjectAccess soa(current); // Now we know we have the shared lock. + + // Decode all threads to raw pointers. Put them into a handle scope to avoid any moving GC bugs. + art::VariableSizedHandleScope hs(current); + std::vector<art::Handle<art::mirror::Object>> handles; + for (jint i = 0; i != thread_count; ++i) { + if (thread_list[i] == nullptr) { + return ERR(INVALID_THREAD); + } + if (!soa.Env()->IsInstanceOf(thread_list[i], art::WellKnownClasses::java_lang_Thread)) { + return ERR(INVALID_THREAD); + } + handles.push_back(hs.NewHandle(soa.Decode<art::mirror::Object>(thread_list[i]))); + } + + std::vector<art::Thread*> threads; + std::vector<size_t> thread_list_indices; + std::vector<std::vector<jvmtiFrameInfo>> frames; + + { + art::ScopedThreadSuspension sts(current, art::kWaitingForDebuggerSuspension); + art::ScopedSuspendAll ssa("GetThreadListStackTraces"); + + { + std::list<art::Thread*> art_thread_list; + { + art::MutexLock mu(current, *art::Locks::thread_list_lock_); + art_thread_list = art::Runtime::Current()->GetThreadList()->GetList(); + } + + for (art::Thread* thread : art_thread_list) { + if (thread->IsStillStarting()) { + // Skip this. We can't get the jpeer, and if it is for a thread in the thread_list, + // we'll just report STARTING. + continue; + } + + // Get the peer, and check whether we know it. + art::ObjPtr<art::mirror::Object> peer = thread->GetPeer(); + for (size_t index = 0; index != handles.size(); ++index) { + if (peer == handles[index].Get()) { + // Found the thread. + GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count)); + thread->RequestSynchronousCheckpoint(&closure); + + threads.push_back(thread); + thread_list_indices.push_back(index); + frames.emplace_back(); + frames.back().swap(closure.frames); + + continue; + } + } + + // Must be not started, or dead. We'll deal with it at the end. + } + } + } + + // Convert the data into our output format. + + // Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to + // allocate one big chunk for this and the actual frames, which means we need + // to either be conservative or rearrange things later (the latter is implemented). + std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[frames.size()]); + std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos; + frame_infos.reserve(frames.size()); + + // Now run through and add data for each thread. + size_t sum_frames = 0; + for (size_t index = 0; index < frames.size(); ++index) { + jvmtiStackInfo& stack_info = stack_info_array.get()[index]; + memset(&stack_info, 0, sizeof(jvmtiStackInfo)); + + art::Thread* self = threads[index]; + const std::vector<jvmtiFrameInfo>& thread_frames = frames[index]; + + // For the time being, set the thread to null. We don't have good ScopedLocalRef + // infrastructure. + DCHECK(self->GetPeer() != nullptr); + stack_info.thread = nullptr; + stack_info.state = JVMTI_THREAD_STATE_SUSPENDED; + + size_t collected_frames = thread_frames.size(); + if (max_frame_count == 0 || collected_frames == 0) { + stack_info.frame_count = 0; + stack_info.frame_buffer = nullptr; + continue; + } + DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count)); + + jvmtiFrameInfo* frame_info = new jvmtiFrameInfo[collected_frames]; + frame_infos.emplace_back(frame_info); + + jint count; + jvmtiError translate_result = TranslateFrameVector(thread_frames, + 0, + 0, + static_cast<jint>(collected_frames), + frame_info, + &count); + DCHECK(translate_result == JVMTI_ERROR_NONE); + stack_info.frame_count = static_cast<jint>(collected_frames); + stack_info.frame_buffer = frame_info; + sum_frames += static_cast<size_t>(count); + } + + // No errors, yet. Now put it all into an output buffer. Note that this is not frames.size(), + // potentially. + size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * thread_count, + alignof(jvmtiFrameInfo)); + size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo); + unsigned char* chunk_data; + jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data); + if (alloc_result != ERR(NONE)) { + return alloc_result; + } + + jvmtiStackInfo* stack_info = reinterpret_cast<jvmtiStackInfo*>(chunk_data); + jvmtiFrameInfo* frame_info = reinterpret_cast<jvmtiFrameInfo*>( + chunk_data + rounded_stack_info_size); + + for (size_t i = 0; i < static_cast<size_t>(thread_count); ++i) { + // Check whether we found a running thread for this. + // Note: For simplicity, and with the expectation that the list is usually small, use a simple + // search. (The list is *not* sorted!) + auto it = std::find(thread_list_indices.begin(), thread_list_indices.end(), i); + if (it == thread_list_indices.end()) { + // No native thread. Must be new or dead. We need to fill out the stack info now. + // (Need to read the Java "started" field to know whether this is starting or terminated.) + art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread_list[i]); + art::ObjPtr<art::mirror::Class> klass = peer->GetClass(); + art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z"); + CHECK(started_field != nullptr); + bool started = started_field->GetBoolean(peer) != 0; + constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW; + constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED | + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED; + stack_info[i].thread = reinterpret_cast<JNIEnv*>(soa.Env())->NewLocalRef(thread_list[i]); + stack_info[i].state = started ? kTerminatedState : kStartedState; + stack_info[i].frame_count = 0; + stack_info[i].frame_buffer = nullptr; + } else { + // Had a native thread and frames. + size_t f_index = it - thread_list_indices.begin(); + + jvmtiStackInfo& old_stack_info = stack_info_array.get()[f_index]; + jvmtiStackInfo& new_stack_info = stack_info[i]; + + memcpy(&new_stack_info, &old_stack_info, sizeof(jvmtiStackInfo)); + new_stack_info.thread = reinterpret_cast<JNIEnv*>(soa.Env())->NewLocalRef(thread_list[i]); + if (old_stack_info.frame_count > 0) { + // Only copy when there's data - leave the nullptr alone. + size_t frames_size = + static_cast<size_t>(old_stack_info.frame_count) * sizeof(jvmtiFrameInfo); + memcpy(frame_info, old_stack_info.frame_buffer, frames_size); + new_stack_info.frame_buffer = frame_info; + frame_info += old_stack_info.frame_count; + } + } + } + + * stack_info_ptr = stack_info; + + return ERR(NONE); +} + +// Walks up the stack counting Java frames. This is not StackVisitor::ComputeNumFrames, as +// runtime methods and transitions must not be counted. +struct GetFrameCountVisitor : public art::StackVisitor { + explicit GetFrameCountVisitor(art::Thread* thread) + : art::StackVisitor(thread, nullptr, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames), + count(0) {} + + bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ArtMethod* m = GetMethod(); + const bool do_count = !(m == nullptr || m->IsRuntimeMethod()); + if (do_count) { + count++; + } + return true; + } + + size_t count; +}; + +struct GetFrameCountClosure : public art::Closure { + public: + GetFrameCountClosure() : count(0) {} + + void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + GetFrameCountVisitor visitor(self); + visitor.WalkStack(false); + + count = visitor.count; + } + + size_t count; +}; + +jvmtiError StackUtil::GetFrameCount(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread java_thread, + jint* count_ptr) { + art::Thread* thread; + jvmtiError thread_error = GetThread(art::Thread::Current()->GetJniEnv(), java_thread, &thread); + if (thread_error != ERR(NONE)) { + return thread_error; + } + DCHECK(thread != nullptr); + + if (count_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + GetFrameCountClosure closure; + thread->RequestSynchronousCheckpoint(&closure); + + *count_ptr = closure.count; + return ERR(NONE); +} + +// Walks up the stack 'n' callers, when used with Thread::WalkStack. +struct GetLocationVisitor : public art::StackVisitor { + GetLocationVisitor(art::Thread* thread, size_t n_in) + : art::StackVisitor(thread, nullptr, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames), + n(n_in), + count(0), + caller(nullptr), + caller_dex_pc(0) {} + + bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ArtMethod* m = GetMethod(); + const bool do_count = !(m == nullptr || m->IsRuntimeMethod()); + if (do_count) { + DCHECK(caller == nullptr); + if (count == n) { + caller = m; + caller_dex_pc = GetDexPc(false); + return false; + } + count++; + } + return true; + } + + const size_t n; + size_t count; + art::ArtMethod* caller; + uint32_t caller_dex_pc; +}; + +struct GetLocationClosure : public art::Closure { + public: + explicit GetLocationClosure(size_t n_in) : n(n_in), method(nullptr), dex_pc(0) {} + + void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + GetLocationVisitor visitor(self, n); + visitor.WalkStack(false); + + method = visitor.caller; + dex_pc = visitor.caller_dex_pc; + } + + const size_t n; + art::ArtMethod* method; + uint32_t dex_pc; +}; + +jvmtiError StackUtil::GetFrameLocation(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread java_thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr) { + art::Thread* thread; + jvmtiError thread_error = GetThread(art::Thread::Current()->GetJniEnv(), java_thread, &thread); + if (thread_error != ERR(NONE)) { + return thread_error; + } + DCHECK(thread != nullptr); + + if (depth < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + if (method_ptr == nullptr || location_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + GetLocationClosure closure(static_cast<size_t>(depth)); + thread->RequestSynchronousCheckpoint(&closure); + + if (closure.method == nullptr) { + return ERR(NO_MORE_FRAMES); + } + + *method_ptr = art::jni::EncodeArtMethod(closure.method); + if (closure.method->IsNative()) { + *location_ptr = -1; + } else { + if (closure.dex_pc == art::DexFile::kDexNoIndex) { + return ERR(INTERNAL); + } + *location_ptr = static_cast<jlocation>(closure.dex_pc); + } + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_stack.h b/runtime/openjdkjvmti/ti_stack.h index 7619f98daf..6a593cfa9c 100644 --- a/runtime/openjdkjvmti/ti_stack.h +++ b/runtime/openjdkjvmti/ti_stack.h @@ -47,12 +47,26 @@ class StackUtil { jint* thread_count_ptr) REQUIRES(!art::Locks::thread_list_lock_); + static jvmtiError GetFrameCount(jvmtiEnv* env, jthread thread, jint* count_ptr); + + static jvmtiError GetFrameLocation(jvmtiEnv* env, + jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr); + static jvmtiError GetStackTrace(jvmtiEnv* env, jthread thread, jint start_depth, jint max_frame_count, jvmtiFrameInfo* frame_buffer, jint* count_ptr); + + static jvmtiError GetThreadListStackTraces(jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index e20f5605d8..970cc24f12 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -35,13 +35,17 @@ #include "art_jvmti.h" #include "base/logging.h" #include "base/mutex.h" +#include "gc/system_weak.h" +#include "gc_root-inl.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" #include "obj_ptr.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" +#include "thread_list.h" #include "well_known_classes.h" namespace openjdkjvmti { @@ -354,4 +358,165 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError ThreadUtil::GetAllThreads(jvmtiEnv* env, + jint* threads_count_ptr, + jthread** threads_ptr) { + if (threads_count_ptr == nullptr || threads_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Thread* current = art::Thread::Current(); + + art::ScopedObjectAccess soa(current); + + art::MutexLock mu(current, *art::Locks::thread_list_lock_); + std::list<art::Thread*> thread_list = art::Runtime::Current()->GetThreadList()->GetList(); + + std::vector<art::ObjPtr<art::mirror::Object>> peers; + + for (art::Thread* thread : thread_list) { + // Skip threads that are still starting. + if (thread->IsStillStarting()) { + continue; + } + + art::ObjPtr<art::mirror::Object> peer = thread->GetPeer(); + if (peer != nullptr) { + peers.push_back(peer); + } + } + + if (peers.empty()) { + *threads_count_ptr = 0; + *threads_ptr = nullptr; + } else { + unsigned char* data; + jvmtiError data_result = env->Allocate(peers.size() * sizeof(jthread), &data); + if (data_result != ERR(NONE)) { + return data_result; + } + jthread* threads = reinterpret_cast<jthread*>(data); + for (size_t i = 0; i != peers.size(); ++i) { + threads[i] = soa.AddLocalReference<jthread>(peers[i]); + } + + *threads_count_ptr = static_cast<jint>(peers.size()); + *threads_ptr = threads; + } + return ERR(NONE); +} + +jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread thread, + const void* data) { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Thread* self = GetNativeThread(thread, soa); + if (self == nullptr && thread == nullptr) { + return ERR(INVALID_THREAD); + } + if (self == nullptr) { + return ERR(THREAD_NOT_ALIVE); + } + + self->SetCustomTLS(data); + + return ERR(NONE); +} + +jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread thread, + void** data_ptr) { + if (data_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Thread* self = GetNativeThread(thread, soa); + if (self == nullptr && thread == nullptr) { + return ERR(INVALID_THREAD); + } + if (self == nullptr) { + return ERR(THREAD_NOT_ALIVE); + } + + *data_ptr = const_cast<void*>(self->GetCustomTLS()); + return ERR(NONE); +} + +struct AgentData { + const void* arg; + jvmtiStartFunction proc; + jthread thread; + JavaVM* java_vm; + jvmtiEnv* jvmti_env; + jint priority; +}; + +static void* AgentCallback(void* arg) { + std::unique_ptr<AgentData> data(reinterpret_cast<AgentData*>(arg)); + CHECK(data->thread != nullptr); + + // We already have a peer. So call our special Attach function. + art::Thread* self = art::Thread::Attach("JVMTI Agent thread", true, data->thread); + CHECK(self != nullptr); + // The name in Attach() is only for logging. Set the thread name. This is important so + // that the thread is no longer seen as starting up. + { + art::ScopedObjectAccess soa(self); + self->SetThreadName("JVMTI Agent thread"); + } + + // Release the peer. + JNIEnv* env = self->GetJniEnv(); + env->DeleteGlobalRef(data->thread); + data->thread = nullptr; + + // Run the agent code. + data->proc(data->jvmti_env, env, const_cast<void*>(data->arg)); + + // Detach the thread. + int detach_result = data->java_vm->DetachCurrentThread(); + CHECK_EQ(detach_result, 0); + + return nullptr; +} + +jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority) { + if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { + return ERR(INVALID_PRIORITY); + } + JNIEnv* env = art::Thread::Current()->GetJniEnv(); + if (thread == nullptr || !env->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) { + return ERR(INVALID_THREAD); + } + if (proc == nullptr) { + return ERR(NULL_POINTER); + } + + std::unique_ptr<AgentData> data(new AgentData); + data->arg = arg; + data->proc = proc; + // We need a global ref for Java objects, as local refs will be invalid. + data->thread = env->NewGlobalRef(thread); + data->java_vm = art::Runtime::Current()->GetJavaVM(); + data->jvmti_env = jvmti_env; + data->priority = priority; + + pthread_t pthread; + int pthread_create_result = pthread_create(&pthread, + nullptr, + &AgentCallback, + reinterpret_cast<void*>(data.get())); + if (pthread_create_result != 0) { + return ERR(INTERNAL); + } + data.release(); + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h index b6ffbb5f65..5aaec583da 100644 --- a/runtime/openjdkjvmti/ti_thread.h +++ b/runtime/openjdkjvmti/ti_thread.h @@ -39,11 +39,22 @@ namespace openjdkjvmti { class ThreadUtil { public: + static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr); + static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr); static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr); static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr); + + static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data); + static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr); + + static jvmtiError RunAgentThread(jvmtiEnv* env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc new file mode 100644 index 0000000000..35b1bfd920 --- /dev/null +++ b/runtime/openjdkjvmti/ti_threadgroup.cc @@ -0,0 +1,285 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_threadgroup.h" + +#include "art_field.h" +#include "art_jvmti.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "handle_scope-inl.h" +#include "jni_internal.h" +#include "mirror/class.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "obj_ptr.h" +#include "object_lock.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" +#include "well_known_classes.h" + +namespace openjdkjvmti { + + +jvmtiError ThreadGroupUtil::GetTopThreadGroups(jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + // We only have a single top group. So we can take the current thread and move upwards. + if (group_count_ptr == nullptr || groups_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Runtime* runtime = art::Runtime::Current(); + if (runtime == nullptr) { + // Must be starting the runtime, or dying. + return ERR(WRONG_PHASE); + } + + jobject sys_thread_group = runtime->GetSystemThreadGroup(); + if (sys_thread_group == nullptr) { + // Seems we're still starting up. + return ERR(WRONG_PHASE); + } + + unsigned char* data; + jvmtiError result = env->Allocate(sizeof(jthreadGroup), &data); + if (result != ERR(NONE)) { + return result; + } + + jthreadGroup* groups = reinterpret_cast<jthreadGroup*>(data); + *groups = + reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv())->NewLocalRef(sys_thread_group); + *groups_ptr = groups; + *group_count_ptr = 1; + + return ERR(NONE); +} + +jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + if (group == nullptr) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(group); + + // Do the name first. It's the only thing that can fail. + { + art::ArtField* name_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name); + CHECK(name_field != nullptr); + art::ObjPtr<art::mirror::String> name_obj = + art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj)); + std::string tmp_str; + const char* tmp_cstr; + if (name_obj == nullptr) { + tmp_cstr = ""; + } else { + tmp_str = name_obj->ToModifiedUtf8(); + tmp_cstr = tmp_str.c_str(); + } + jvmtiError result = + CopyString(env, tmp_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name)); + if (result != ERR(NONE)) { + return result; + } + } + + // Parent. + { + art::ArtField* parent_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent); + CHECK(parent_field != nullptr); + art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj); + info_ptr->parent = parent_group == nullptr + ? nullptr + : soa.AddLocalReference<jthreadGroup>(parent_group); + } + + // Max priority. + { + art::ArtField* prio_field = obj->GetClass()->FindDeclaredInstanceField("maxPriority", "I"); + CHECK(prio_field != nullptr); + info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj)); + } + + // Daemon. + { + art::ArtField* daemon_field = obj->GetClass()->FindDeclaredInstanceField("daemon", "Z"); + CHECK(daemon_field != nullptr); + info_ptr->is_daemon = daemon_field->GetBoolean(obj) == 0 ? JNI_FALSE : JNI_TRUE; + } + + return ERR(NONE); +} + + +static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thread_group, + art::ObjPtr<art::mirror::Object> peer) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + CHECK(desired_thread_group.Get() != nullptr); + + art::ArtField* thread_group_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + DCHECK(thread_group_field != nullptr); + art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer); + return (group == desired_thread_group.Get()); +} + +static void GetThreads(art::Handle<art::mirror::Object> thread_group, + std::vector<art::ObjPtr<art::mirror::Object>>* thread_peers) + REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) { + CHECK(thread_group.Get() != nullptr); + + art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_); + for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) { + if (t->IsStillStarting()) { + continue; + } + art::ObjPtr<art::mirror::Object> peer = t->GetPeer(); + if (peer == nullptr) { + continue; + } + if (IsInDesiredThreadGroup(thread_group, peer)) { + thread_peers->push_back(peer); + } + } +} + +static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group, + std::vector<art::ObjPtr<art::mirror::Object>>* thread_groups) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + CHECK(thread_group.Get() != nullptr); + + // Get the ThreadGroup[] "groups" out of this thread group... + art::ArtField* groups_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups); + art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get()); + + if (groups_array == nullptr) { + return; + } + CHECK(groups_array->IsObjectArray()); + + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> groups_array_as_array = + groups_array->AsObjectArray<art::mirror::Object>(); + + // Copy all non-null elements. + for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) { + art::ObjPtr<art::mirror::Object> entry = groups_array_as_array->Get(i); + if (entry != nullptr) { + thread_groups->push_back(entry); + } + } +} + +jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + if (group == nullptr) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + + if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) { + return ERR(INVALID_THREAD_GROUP); + } + + art::StackHandleScope<1> hs(soa.Self()); + art::Handle<art::mirror::Object> thread_group = hs.NewHandle( + soa.Decode<art::mirror::Object>(group)); + + art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group); + + std::vector<art::ObjPtr<art::mirror::Object>> thread_peers; + GetThreads(thread_group, &thread_peers); + + std::vector<art::ObjPtr<art::mirror::Object>> thread_groups; + GetChildThreadGroups(thread_group, &thread_groups); + + jthread* thread_data = nullptr; + JvmtiUniquePtr peers_uptr; + if (!thread_peers.empty()) { + unsigned char* data; + jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data); + if (res != ERR(NONE)) { + return res; + } + thread_data = reinterpret_cast<jthread*>(data); + peers_uptr = MakeJvmtiUniquePtr(env, data); + } + + jthreadGroup* group_data = nullptr; + if (!thread_groups.empty()) { + unsigned char* data; + jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data); + if (res != ERR(NONE)) { + return res; + } + group_data = reinterpret_cast<jthreadGroup*>(data); + } + + // Can't fail anymore from here on. + + // Copy data into out buffers. + for (size_t i = 0; i != thread_peers.size(); ++i) { + thread_data[i] = soa.AddLocalReference<jthread>(thread_peers[i]); + } + for (size_t i = 0; i != thread_groups.size(); ++i) { + group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]); + } + + *thread_count_ptr = static_cast<jint>(thread_peers.size()); + *threads_ptr = thread_data; + *group_count_ptr = static_cast<jint>(thread_groups.size()); + *groups_ptr = group_data; + + // Everything's fine. + peers_uptr.release(); + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_threadgroup.h b/runtime/openjdkjvmti/ti_threadgroup.h new file mode 100644 index 0000000000..c3a0ff5e15 --- /dev/null +++ b/runtime/openjdkjvmti/ti_threadgroup.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class ThreadGroupUtil { + public: + static jvmtiError GetTopThreadGroups(jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ diff --git a/runtime/openjdkjvmti/ti_timers.cc b/runtime/openjdkjvmti/ti_timers.cc new file mode 100644 index 0000000000..24fb0419ee --- /dev/null +++ b/runtime/openjdkjvmti/ti_timers.cc @@ -0,0 +1,93 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_timers.h" + +#include <limits> + +#ifndef __APPLE__ +#include <time.h> +#else +#include <sys/time.h> +#endif +#include <unistd.h> + +#include "art_jvmti.h" +#include "base/macros.h" + +namespace openjdkjvmti { + +jvmtiError TimerUtil::GetAvailableProcessors(jvmtiEnv* env ATTRIBUTE_UNUSED, + jint* processor_count_ptr) { + if (processor_count_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + *processor_count_ptr = static_cast<jint>(sysconf(_SC_NPROCESSORS_CONF)); + + return ERR(NONE); +} + +jvmtiError TimerUtil::GetTimerInfo(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiTimerInfo* info_ptr) { + if (info_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + info_ptr->max_value = static_cast<jlong>(std::numeric_limits<uint64_t>::max()); + info_ptr->may_skip_forward = JNI_TRUE; + info_ptr->may_skip_backward = JNI_TRUE; + info_ptr->kind = jvmtiTimerKind::JVMTI_TIMER_ELAPSED; + + return ERR(NONE); +} + +jvmtiError TimerUtil::GetTime(jvmtiEnv* env ATTRIBUTE_UNUSED, jlong* nanos_ptr) { + if (nanos_ptr == nullptr) { + return ERR(NULL_POINTER); + } + +#ifndef __APPLE__ + // Use the same implementation as System.nanoTime. + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + *nanos_ptr = now.tv_sec * 1000000000LL + now.tv_nsec; +#else + // No CLOCK_MONOTONIC support on older Mac OS. + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + *nanos_ptr = static_cast<jlong>(t.tv_sec)*1000000000LL + static_cast<jlong>(t.tv_usec)*1000LL; +#endif + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_timers.h b/runtime/openjdkjvmti/ti_timers.h new file mode 100644 index 0000000000..6300678ff7 --- /dev/null +++ b/runtime/openjdkjvmti/ti_timers.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class TimerUtil { + public: + static jvmtiError GetAvailableProcessors(jvmtiEnv* env, jint* processor_count_ptr); + + static jvmtiError GetTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr); + + static jvmtiError GetTime(jvmtiEnv* env, jlong* nanos_ptr); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_ diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index f5451254c0..af4fb7187a 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -38,6 +38,7 @@ #include "class_linker.h" #include "dex_file.h" #include "dex_file_types.h" +#include "events-inl.h" #include "gc_root-inl.h" #include "globals.h" #include "jni_env_ext-inl.h" @@ -46,18 +47,83 @@ #include "mem_map.h" #include "mirror/array.h" #include "mirror/class-inl.h" +#include "mirror/class_ext.h" #include "mirror/class_loader-inl.h" #include "mirror/string-inl.h" #include "oat_file.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread_list.h" +#include "ti_redefine.h" #include "transform.h" #include "utf.h" #include "utils/dex_cache_arrays_layout-inl.h" namespace openjdkjvmti { +jvmtiError Transformer::RetransformClassesDirect( + ArtJvmTiEnv* env, + art::Thread* self, + /*in-out*/std::vector<ArtClassDefinition>* definitions) { + 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)); + def.SetNewDexData(env, new_len, new_data); + } + return OK; +} + +jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + jint class_count, + const jclass* classes, + /*out*/std::string* error_msg) { + if (env == nullptr) { + *error_msg = "env was null!"; + return ERR(INVALID_ENVIRONMENT); + } else if (class_count < 0) { + *error_msg = "class_count was less then 0"; + return ERR(ILLEGAL_ARGUMENT); + } else if (class_count == 0) { + // We don't actually need to do anything. Just return OK. + return OK; + } else if (classes == nullptr) { + *error_msg = "null classes!"; + return ERR(NULL_POINTER); + } + // A holder that will Deallocate all the class bytes buffers on destruction. + std::vector<ArtClassDefinition> definitions; + jvmtiError res = OK; + for (jint i = 0; i < class_count; i++) { + ArtClassDefinition def; + res = FillInTransformationData(env, classes[i], &def); + if (res != OK) { + return res; + } + definitions.push_back(std::move(def)); + } + res = RetransformClassesDirect(env, self, &definitions); + if (res != OK) { + return res; + } + return Redefiner::RedefineClassesDirect(env, runtime, self, definitions, error_msg); +} + +// TODO Move this somewhere else, ti_class? jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location) { JNIEnv* jni_env = nullptr; jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(&jni_env), JNI_VERSION_1_1); @@ -73,42 +139,74 @@ 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, + /*out*/unsigned char** dex_data) { + art::StackHandleScope<2> hs(art::Thread::Current()); + art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData())); + if (!ext.IsNull()) { + art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes())); + if (!orig_dex.IsNull()) { + *dex_data_len = static_cast<jint>(orig_dex->GetLength()); + return CopyDataIntoJvmtiBuffer(env, + reinterpret_cast<const unsigned char*>(orig_dex->GetData()), + *dex_data_len, + /*out*/dex_data); + } + } + // TODO De-quicken the dex file before passing it to the agents. + LOG(WARNING) << "Dex file is not de-quickened yet! Quickened dex instructions might be present"; + const art::DexFile& dex = klass->GetDexFile(); + *dex_data_len = static_cast<jint>(dex.Size()); + return CopyDataIntoJvmtiBuffer(env, dex.Begin(), *dex_data_len, /*out*/dex_data); +} + // TODO Move this function somewhere more appropriate. // Gets the data surrounding the given class. -jvmtiError GetTransformationData(ArtJvmTiEnv* env, - jclass klass, - /*out*/std::string* location, - /*out*/JNIEnv** jni_env_ptr, - /*out*/jobject* loader, - /*out*/std::string* name, - /*out*/jobject* protection_domain, - /*out*/jint* data_len, - /*out*/unsigned char** dex_data) { - jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(jni_env_ptr), JNI_VERSION_1_1); - if (ret != JNI_OK) { +// TODO Make this less magical. +jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env, + jclass klass, + ArtClassDefinition* def) { + JNIEnv* jni_env = GetJniEnv(env); + if (jni_env == nullptr) { // TODO Different error might be better? return ERR(INTERNAL); } - JNIEnv* jni_env = *jni_env_ptr; art::ScopedObjectAccess soa(jni_env); art::StackHandleScope<3> hs(art::Thread::Current()); art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass))); - *loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader()); - *name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8(); + if (hs_klass.IsNull()) { + return ERR(INVALID_CLASS); + } + def->klass = klass; + def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader()); + def->name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8(); // TODO is this always null? - *protection_domain = nullptr; - const art::DexFile& dex = hs_klass->GetDexFile(); - *location = dex.GetLocation(); - *data_len = static_cast<jint>(dex.Size()); - // TODO We should maybe change env->Allocate to allow us to mprotect this memory and stop writes. - jvmtiError alloc_error = env->Allocate(*data_len, dex_data); - if (alloc_error != OK) { - return alloc_error; + def->protection_domain = nullptr; + if (def->dex_data.get() == nullptr) { + unsigned char* new_data; + jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data); + if (res == OK) { + def->dex_data = MakeJvmtiUniquePtr(env, new_data); + } else { + return res; + } } - // Copy the data into a temporary buffer. - memcpy(reinterpret_cast<void*>(*dex_data), - reinterpret_cast<const void*>(dex.Begin()), - *data_len); return OK; } diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h index 0ad5099daf..65f2ae1353 100644 --- a/runtime/openjdkjvmti/transform.h +++ b/runtime/openjdkjvmti/transform.h @@ -37,22 +37,37 @@ #include <jni.h> #include "art_jvmti.h" +#include "ti_class_definition.h" #include "jvmti.h" namespace openjdkjvmti { jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location); -// Gets the data surrounding the given class. -jvmtiError GetTransformationData(ArtJvmTiEnv* env, - jclass klass, - /*out*/std::string* location, - /*out*/JNIEnv** jni_env_ptr, - /*out*/jobject* loader, - /*out*/std::string* name, - /*out*/jobject* protection_domain, - /*out*/jint* data_len, - /*out*/unsigned char** dex_data); +class Transformer { + public: + static jvmtiError RetransformClassesDirect( + ArtJvmTiEnv* env, art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions); + + static jvmtiError RetransformClasses(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + jint class_count, + const jclass* classes, + /*out*/std::string* error_msg); + + // Gets the data surrounding the given class. + static jvmtiError FillInTransformationData(ArtJvmTiEnv* env, + jclass klass, + ArtClassDefinition* def); + + private: + static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env, + art::Handle<art::mirror::Class> klass, + /*out*/jint* dex_data_length, + /*out*/unsigned char** dex_data) + REQUIRES_SHARED(art::Locks::mutator_lock_); +}; } // namespace openjdkjvmti diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index e1022b000b..a72159bc80 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -763,8 +763,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { "(Enable new and experimental agent support)\n"); UsageMessage(stream, " -Xexperimental:agents" "(Enable new and experimental agent support)\n"); - UsageMessage(stream, " -Xexperimental:method-handles" - "(Enable new and experimental method handles support)\n"); UsageMessage(stream, "\n"); UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 4d2450135e..a2b4cb37a9 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -216,43 +216,54 @@ class ArgArray { } bool BuildArgArrayFromObjectArray(ObjPtr<mirror::Object> receiver, - ObjPtr<mirror::ObjectArray<mirror::Object>> args, - ArtMethod* m) + ObjPtr<mirror::ObjectArray<mirror::Object>> raw_args, + ArtMethod* m, + Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile::TypeList* classes = m->GetParameterTypeList(); // Set receiver if non-null (method is not static) if (receiver != nullptr) { Append(receiver); } + StackHandleScope<2> hs(self); + MutableHandle<mirror::Object> arg(hs.NewHandle<mirror::Object>(nullptr)); + Handle<mirror::ObjectArray<mirror::Object>> args( + hs.NewHandle<mirror::ObjectArray<mirror::Object>>(raw_args)); for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { - ObjPtr<mirror::Object> arg(args->Get(args_offset)); - if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + arg.Assign(args->Get(args_offset)); + if (((shorty_[i] == 'L') && (arg.Get() != nullptr)) || + ((arg.Get() == nullptr && shorty_[i] != 'L'))) { + // TODO: The method's parameter's type must have been previously resolved, yet + // we've seen cases where it's not b/34440020. ObjPtr<mirror::Class> dst_class( m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_, - true /* resolve */, - pointer_size)); - if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) { + true /* resolve */)); + if (dst_class.Ptr() == nullptr) { + CHECK(self->IsExceptionPending()); + return false; + } + if (UNLIKELY(arg.Get() == nullptr || !arg->InstanceOf(dst_class))) { ThrowIllegalArgumentException( StringPrintf("method %s argument %zd has type %s, got %s", m->PrettyMethod(false).c_str(), args_offset + 1, // Humans don't count from 0. mirror::Class::PrettyDescriptor(dst_class).c_str(), - mirror::Object::PrettyTypeOf(arg).c_str()).c_str()); + mirror::Object::PrettyTypeOf(arg.Get()).c_str()).c_str()); return false; } } #define DO_FIRST_ARG(match_descriptor, get_fn, append) { \ - if (LIKELY(arg != nullptr && arg->GetClass()->DescriptorEquals(match_descriptor))) { \ + if (LIKELY(arg.Get() != nullptr && \ + arg->GetClass()->DescriptorEquals(match_descriptor))) { \ ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \ - append(primitive_field-> get_fn(arg)); + append(primitive_field-> get_fn(arg.Get())); #define DO_ARG(match_descriptor, get_fn, append) \ - } else if (LIKELY(arg != nullptr && \ + } else if (LIKELY(arg.Get() != nullptr && \ arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \ ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \ - append(primitive_field-> get_fn(arg)); + append(primitive_field-> get_fn(arg.Get())); #define DO_FAIL(expected) \ } else { \ @@ -266,14 +277,14 @@ class ArgArray { ArtMethod::PrettyMethod(m, false).c_str(), \ args_offset + 1, \ expected, \ - mirror::Object::PrettyTypeOf(arg).c_str()).c_str()); \ + mirror::Object::PrettyTypeOf(arg.Get()).c_str()).c_str()); \ } \ return false; \ } } switch (shorty_[i]) { case 'L': - Append(arg); + Append(arg.Get()); break; case 'Z': DO_FIRST_ARG("Ljava/lang/Boolean;", GetBoolean, Append) @@ -363,12 +374,9 @@ static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args) } // TODO: If args contain object references, it may cause problems. Thread* const self = Thread::Current(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (uint32_t i = 0; i < num_params; i++) { dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_; - ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, - true /* resolve*/, - pointer_size)); + ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */)); if (param_type == nullptr) { CHECK(self->IsExceptionPending()); LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: " @@ -649,7 +657,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM uint32_t shorty_len = 0; const char* shorty = np_method->GetShorty(&shorty_len); ArgArray arg_array(shorty, shorty_len); - if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method)) { + if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) { CHECK(soa.Self()->IsExceptionPending()); return nullptr; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index df5fc5ce8a..4936a2f9a7 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -137,6 +137,7 @@ #include "jit/profile_saver.h" #include "quick/quick_method_frame_info.h" #include "reflection.h" +#include "runtime_callbacks.h" #include "runtime_options.h" #include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" @@ -253,10 +254,12 @@ Runtime::Runtime() pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), - zygote_no_threads_(false) { + zygote_no_threads_(false), + cha_(nullptr) { CheckAsmSupportOffsetsAndSizes(); std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u); interpreter::CheckInterpreterAsmConstants(); + callbacks_.reset(new RuntimeCallbacks()); } Runtime::~Runtime() { @@ -301,6 +304,13 @@ Runtime::~Runtime() { Trace::Shutdown(); + // Report death. Clients me require a working thread, still, so do it before GC completes and + // all non-daemon threads are done. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kDeath); + } + if (attach_shutdown_thread) { DetachCurrentThread(); self = nullptr; @@ -703,6 +713,13 @@ bool Runtime::Start() { Thread::FinishStartup(); + // Send the start phase event. We have to wait till here as this is when the main thread peer + // has just been generated, important root clinits have been run and JNI is completely functional. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kStart); + } + system_class_loader_ = CreateSystemClassLoader(this); if (!is_zygote_) { @@ -739,6 +756,12 @@ bool Runtime::Start() { 0); } + // Send the initialized phase event. + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit); + } + return true; } @@ -1100,6 +1123,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { if (runtime_options.Exists(Opt::JdwpOptions)) { Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions)); } + callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); + callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback()); jit_options_.reset(jit::JitOptions::CreateFromRuntimeArguments(runtime_options)); if (IsAotCompiler()) { @@ -1358,6 +1383,10 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { LOG(ERROR) << "Unable to load an agent: " << err; } } + { + ScopedObjectAccess soa(self); + callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents); + } VLOG(startup) << "Runtime::Init exiting"; @@ -1370,7 +1399,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so"; // Is the plugin already loaded? - for (Plugin p : *plugins) { + for (const Plugin& p : *plugins) { if (p.GetLibrary() == plugin_name) { return true; } @@ -1384,11 +1413,6 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, Plugin new_plugin = Plugin::Create(plugin_name); - // Suspend all threads to protect ourself somewhat. - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); // Now we know we have the shared lock. - ScopedThreadSuspension sts(self, art::kWaitingForDebuggerToAttach); - ScopedSuspendAll ssa("EnsureJvmtiPlugin"); if (!new_plugin.Load(error_msg)) { return false; } @@ -1552,6 +1576,12 @@ void Runtime::DumpForSigQuit(std::ostream& os) { thread_list_->DumpForSigQuit(os); BaseMutex::DumpAll(os); + + // Inform anyone else who is interested in SigQuit. + { + ScopedObjectAccess soa(Thread::Current()); + callbacks_->SigQuit(); + } } void Runtime::DumpLockHolders(std::ostream& os) { @@ -2235,6 +2265,8 @@ void Runtime::AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder) { gc::ScopedGCCriticalSection gcs(Thread::Current(), gc::kGcCauseAddRemoveSystemWeakHolder, gc::kCollectorTypeAddRemoveSystemWeakHolder); + // Note: The ScopedGCCriticalSection also ensures that the rest of the function is in + // a critical section. system_weak_holders_.push_back(holder); } @@ -2256,4 +2288,8 @@ void Runtime::Aborter(const char* abort_message) { Runtime::Abort(abort_message); } +RuntimeCallbacks* Runtime::GetRuntimeCallbacks() { + return callbacks_.get(); +} + } // namespace art diff --git a/runtime/runtime.h b/runtime/runtime.h index 8fc211c6a3..f7d6810ff5 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -28,6 +28,7 @@ #include "arch/instruction_set.h" #include "base/macros.h" +#include "base/mutex.h" #include "dex_file_types.h" #include "experimental_flags.h" #include "gc_root.h" @@ -40,7 +41,6 @@ #include "process_state.h" #include "quick/quick_method_frame_info.h" #include "runtime_stats.h" -#include "safe_map.h" namespace art { @@ -90,6 +90,7 @@ class NullPointerHandler; class OatFileManager; class Plugin; struct RuntimeArgumentMap; +class RuntimeCallbacks; class SignalCatcher; class StackOverflowHandler; class SuspensionHandler; @@ -304,7 +305,7 @@ class Runtime { } bool IsMethodHandlesEnabled() const { - return experimental_flags_ & ExperimentalFlags::kMethodHandles; + return true; } void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); @@ -660,6 +661,8 @@ class Runtime { void AttachAgent(const std::string& agent_arg); + RuntimeCallbacks* GetRuntimeCallbacks(); + private: static void InitPlatformSignalHandlers(); @@ -917,6 +920,8 @@ class Runtime { ClassHierarchyAnalysis* cha_; + std::unique_ptr<RuntimeCallbacks> callbacks_; + DISALLOW_COPY_AND_ASSIGN(Runtime); }; std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs); diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc new file mode 100644 index 0000000000..7b15a4f1b5 --- /dev/null +++ b/runtime/runtime_callbacks.cc @@ -0,0 +1,104 @@ +/* + * 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 "runtime_callbacks.h" + +#include <algorithm> + +#include "base/macros.h" +#include "class_linker.h" +#include "thread.h" + +namespace art { + +void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) { + thread_callbacks_.push_back(cb); +} + +template <typename T> +ALWAYS_INLINE +static inline void Remove(T* cb, std::vector<T*>* data) { + auto it = std::find(data->begin(), data->end(), cb); + if (it != data->end()) { + data->erase(it); + } +} + +void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) { + Remove(cb, &thread_callbacks_); +} + +void RuntimeCallbacks::ThreadStart(Thread* self) { + for (ThreadLifecycleCallback* cb : thread_callbacks_) { + cb->ThreadStart(self); + } +} + +void RuntimeCallbacks::ThreadDeath(Thread* self) { + for (ThreadLifecycleCallback* cb : thread_callbacks_) { + cb->ThreadDeath(self); + } +} + +void RuntimeCallbacks::AddClassLoadCallback(ClassLoadCallback* cb) { + class_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveClassLoadCallback(ClassLoadCallback* cb) { + Remove(cb, &class_callbacks_); +} + +void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) { + for (ClassLoadCallback* cb : class_callbacks_) { + cb->ClassLoad(klass); + } +} + +void RuntimeCallbacks::ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) { + for (ClassLoadCallback* cb : class_callbacks_) { + cb->ClassPrepare(temp_klass, klass); + } +} + +void RuntimeCallbacks::AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) { + sigquit_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) { + Remove(cb, &sigquit_callbacks_); +} + +void RuntimeCallbacks::SigQuit() { + for (RuntimeSigQuitCallback* cb : sigquit_callbacks_) { + cb->SigQuit(); + } +} + +void RuntimeCallbacks::AddRuntimePhaseCallback(RuntimePhaseCallback* cb) { + phase_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb) { + Remove(cb, &phase_callbacks_); +} + +void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) { + for (RuntimePhaseCallback* cb : phase_callbacks_) { + cb->NextRuntimePhase(phase); + } +} + +} // namespace art diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h new file mode 100644 index 0000000000..e580e7875e --- /dev/null +++ b/runtime/runtime_callbacks.h @@ -0,0 +1,115 @@ +/* + * 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_RUNTIME_CALLBACKS_H_ +#define ART_RUNTIME_RUNTIME_CALLBACKS_H_ + +#include <vector> + +#include "base/macros.h" +#include "base/mutex.h" +#include "handle.h" + +namespace art { + +namespace mirror { +class Class; +} // namespace mirror + +class ClassLoadCallback; +class Thread; +class ThreadLifecycleCallback; + +// Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must +// hold the exclusive lock to add or remove a listener. A thread must hold the shared lock +// to dispatch an event. This setup is chosen as some clients may want to suspend the +// dispatching thread or all threads. +// +// To make this safe, the following restrictions apply: +// * Only the owner of a listener may ever add or remove said listener. +// * A listener must never add or remove itself or any other listener while running. +// * It is the responsibility of the owner to not remove the listener while it is running +// (and suspended). +// +// The simplest way to satisfy these restrictions is to never remove a listener, and to do +// any state checking (is the listener enabled) in the listener itself. For an example, see +// Dbg. + +class RuntimeSigQuitCallback { + public: + virtual ~RuntimeSigQuitCallback() {} + + virtual void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + +class RuntimePhaseCallback { + public: + enum RuntimePhase { + kInitialAgents, // Initial agent loading is done. + kStart, // The runtime is started. + kInit, // The runtime is initialized (and will run user code soon). + kDeath, // The runtime just died. + }; + + virtual ~RuntimePhaseCallback() {} + + virtual void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + +class RuntimeCallbacks { + public: + void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_); + void RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_); + + void ThreadStart(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + void ThreadDeath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + + void AddClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_); + void RemoveClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_); + + void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + void ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_); + + void AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) + REQUIRES(Locks::mutator_lock_); + void RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) + REQUIRES(Locks::mutator_lock_); + + void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_); + + void AddRuntimePhaseCallback(RuntimePhaseCallback* cb) + REQUIRES(Locks::mutator_lock_); + void RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb) + REQUIRES(Locks::mutator_lock_); + + void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + std::vector<ThreadLifecycleCallback*> thread_callbacks_ + GUARDED_BY(Locks::mutator_lock_); + std::vector<ClassLoadCallback*> class_callbacks_ + GUARDED_BY(Locks::mutator_lock_); + std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_ + GUARDED_BY(Locks::mutator_lock_); + std::vector<RuntimePhaseCallback*> phase_callbacks_ + GUARDED_BY(Locks::mutator_lock_); +}; + +} // namespace art + +#endif // ART_RUNTIME_RUNTIME_CALLBACKS_H_ diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc new file mode 100644 index 0000000000..8974b595de --- /dev/null +++ b/runtime/runtime_callbacks_test.cc @@ -0,0 +1,410 @@ +/* + * 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 "runtime_callbacks.h" + +#include "jni.h" +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +#include <initializer_list> +#include <memory> +#include <string> + +#include "art_method-inl.h" +#include "base/mutex.h" +#include "class_linker.h" +#include "common_runtime_test.h" +#include "handle.h" +#include "handle_scope-inl.h" +#include "mem_map.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "obj_ptr.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "ScopedLocalRef.h" +#include "thread-inl.h" +#include "thread_list.h" +#include "well_known_classes.h" + +namespace art { + +class RuntimeCallbacksTest : public CommonRuntimeTest { + protected: + void SetUp() OVERRIDE { + CommonRuntimeTest::SetUp(); + + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach); + ScopedSuspendAll ssa("RuntimeCallbacksTest SetUp"); + AddListener(); + } + + void TearDown() OVERRIDE { + { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach); + ScopedSuspendAll ssa("RuntimeCallbacksTest TearDown"); + RemoveListener(); + } + + CommonRuntimeTest::TearDown(); + } + + virtual void AddListener() REQUIRES(Locks::mutator_lock_) = 0; + virtual void RemoveListener() REQUIRES(Locks::mutator_lock_) = 0; + + void MakeExecutable(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(klass != nullptr); + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + for (auto& m : klass->GetMethods(pointer_size)) { + if (!m.IsAbstract()) { + class_linker_->SetEntryPointsToInterpreter(&m); + } + } + } +}; + +class ThreadLifecycleCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { + public: + static void* PthreadsCallback(void* arg ATTRIBUTE_UNUSED) { + // Attach. + Runtime* runtime = Runtime::Current(); + CHECK(runtime->AttachCurrentThread("ThreadLifecycle test thread", true, nullptr, false)); + + // Detach. + runtime->DetachCurrentThread(); + + // Die... + return nullptr; + } + + protected: + void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->AddThreadLifecycleCallback(&cb_); + } + void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->RemoveThreadLifecycleCallback(&cb_); + } + + enum CallbackState { + kBase, + kStarted, + kDied, + kWrongStart, + kWrongDeath, + }; + + struct Callback : public ThreadLifecycleCallback { + void ThreadStart(Thread* self) OVERRIDE { + if (state == CallbackState::kBase) { + state = CallbackState::kStarted; + stored_self = self; + } else { + state = CallbackState::kWrongStart; + } + } + + void ThreadDeath(Thread* self) OVERRIDE { + if (state == CallbackState::kStarted && self == stored_self) { + state = CallbackState::kDied; + } else { + state = CallbackState::kWrongDeath; + } + } + + Thread* stored_self; + CallbackState state = CallbackState::kBase; + }; + + Callback cb_; +}; + +TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackJava) { + Thread* self = Thread::Current(); + + self->TransitionFromSuspendedToRunnable(); + bool started = runtime_->Start(); + ASSERT_TRUE(started); + + cb_.state = CallbackState::kBase; // Ignore main thread attach. + + { + ScopedObjectAccess soa(self); + MakeExecutable(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread)); + } + + JNIEnv* env = self->GetJniEnv(); + + ScopedLocalRef<jobject> thread_name(env, + env->NewStringUTF("ThreadLifecycleCallback test thread")); + ASSERT_TRUE(thread_name.get() != nullptr); + + ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread)); + ASSERT_TRUE(thread.get() != nullptr); + + env->CallNonvirtualVoidMethod(thread.get(), + WellKnownClasses::java_lang_Thread, + WellKnownClasses::java_lang_Thread_init, + runtime_->GetMainThreadGroup(), + thread_name.get(), + kMinThreadPriority, + JNI_FALSE); + ASSERT_FALSE(env->ExceptionCheck()); + + jmethodID start_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "start", "()V"); + ASSERT_TRUE(start_id != nullptr); + + env->CallVoidMethod(thread.get(), start_id); + ASSERT_FALSE(env->ExceptionCheck()); + + jmethodID join_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "join", "()V"); + ASSERT_TRUE(join_id != nullptr); + + env->CallVoidMethod(thread.get(), join_id); + ASSERT_FALSE(env->ExceptionCheck()); + + EXPECT_TRUE(cb_.state == CallbackState::kDied) << static_cast<int>(cb_.state); +} + +TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackAttach) { + std::string error_msg; + std::unique_ptr<MemMap> stack(MemMap::MapAnonymous("ThreadLifecycleCallback Thread", + nullptr, + 128 * kPageSize, // Just some small stack. + PROT_READ | PROT_WRITE, + false, + false, + &error_msg)); + ASSERT_FALSE(stack == nullptr) << error_msg; + + const char* reason = "ThreadLifecycleCallback test thread"; + pthread_attr_t attr; + CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason); + CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack->Begin(), stack->Size()), reason); + pthread_t pthread; + CHECK_PTHREAD_CALL(pthread_create, + (&pthread, + &attr, + &ThreadLifecycleCallbackRuntimeCallbacksTest::PthreadsCallback, + this), + reason); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason); + + CHECK_PTHREAD_CALL(pthread_join, (pthread, nullptr), "ThreadLifecycleCallback test shutdown"); + + // Detach is not a ThreadDeath event, so we expect to be in state Started. + EXPECT_TRUE(cb_.state == CallbackState::kStarted) << static_cast<int>(cb_.state); +} + +class ClassLoadCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { + protected: + void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->AddClassLoadCallback(&cb_); + } + void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->RemoveClassLoadCallback(&cb_); + } + + bool Expect(std::initializer_list<const char*> list) { + if (cb_.data.size() != list.size()) { + PrintError(list); + return false; + } + + if (!std::equal(cb_.data.begin(), cb_.data.end(), list.begin())) { + PrintError(list); + return false; + } + + return true; + } + + void PrintError(std::initializer_list<const char*> list) { + LOG(ERROR) << "Expected:"; + for (const char* expected : list) { + LOG(ERROR) << " " << expected; + } + LOG(ERROR) << "Found:"; + for (const auto& s : cb_.data) { + LOG(ERROR) << " " << s; + } + } + + struct Callback : public ClassLoadCallback { + void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + std::string tmp; + std::string event = std::string("Load:") + klass->GetDescriptor(&tmp); + data.push_back(event); + } + + void ClassPrepare(Handle<mirror::Class> temp_klass, + Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + std::string tmp, tmp2; + std::string event = std::string("Prepare:") + klass->GetDescriptor(&tmp) + + "[" + temp_klass->GetDescriptor(&tmp2) + "]"; + data.push_back(event); + } + + std::vector<std::string> data; + }; + + Callback cb_; +}; + +TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) { + ScopedObjectAccess soa(Thread::Current()); + jobject jclass_loader = LoadDex("XandY"); + VariableSizedHandleScope hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader))); + + const char* descriptor_y = "LY;"; + Handle<mirror::Class> h_Y( + 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;]" }); + 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;]" }); + EXPECT_TRUE(expect2); +} + +class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { + protected: + void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->AddRuntimeSigQuitCallback(&cb_); + } + void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&cb_); + } + + struct Callback : public RuntimeSigQuitCallback { + void SigQuit() OVERRIDE { + ++sigquit_count; + } + + size_t sigquit_count = 0; + }; + + Callback cb_; +}; + +TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) { + // The runtime needs to be started for the signal handler. + Thread* self = Thread::Current(); + + self->TransitionFromSuspendedToRunnable(); + bool started = runtime_->Start(); + ASSERT_TRUE(started); + + EXPECT_EQ(0u, cb_.sigquit_count); + + kill(getpid(), SIGQUIT); + + // Try a few times. + for (size_t i = 0; i != 30; ++i) { + if (cb_.sigquit_count == 0) { + sleep(1); + } else { + break; + } + } + EXPECT_EQ(1u, cb_.sigquit_count); +} + +class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest { + protected: + void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&cb_); + } + void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) { + Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&cb_); + } + + void TearDown() OVERRIDE { + // Bypass RuntimeCallbacksTest::TearDown, as the runtime is already gone. + CommonRuntimeTest::TearDown(); + } + + struct Callback : public RuntimePhaseCallback { + void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE { + if (p == RuntimePhaseCallback::RuntimePhase::kInitialAgents) { + if (start_seen > 0 || init_seen > 0 || death_seen > 0) { + LOG(FATAL) << "Unexpected order"; + } + ++initial_agents_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kStart) { + if (init_seen > 0 || death_seen > 0) { + LOG(FATAL) << "Init seen before start."; + } + ++start_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kInit) { + ++init_seen; + } else if (p == RuntimePhaseCallback::RuntimePhase::kDeath) { + ++death_seen; + } else { + LOG(FATAL) << "Unknown phase " << static_cast<uint32_t>(p); + } + } + + size_t initial_agents_seen = 0; + size_t start_seen = 0; + size_t init_seen = 0; + size_t death_seen = 0; + }; + + Callback cb_; +}; + +TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) { + ASSERT_EQ(0u, cb_.initial_agents_seen); + ASSERT_EQ(0u, cb_.start_seen); + ASSERT_EQ(0u, cb_.init_seen); + ASSERT_EQ(0u, cb_.death_seen); + + // Start the runtime. + { + Thread* self = Thread::Current(); + self->TransitionFromSuspendedToRunnable(); + bool started = runtime_->Start(); + ASSERT_TRUE(started); + } + + ASSERT_EQ(0u, cb_.initial_agents_seen); + ASSERT_EQ(1u, cb_.start_seen); + ASSERT_EQ(1u, cb_.init_seen); + ASSERT_EQ(0u, cb_.death_seen); + + // Delete the runtime. + runtime_.reset(); + + ASSERT_EQ(0u, cb_.initial_agents_seen); + ASSERT_EQ(1u, cb_.start_seen); + ASSERT_EQ(1u, cb_.init_seen); + ASSERT_EQ(1u, cb_.death_seen); +} + +} // namespace art diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index d1970fe7a2..ecabf9adc5 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -117,7 +117,7 @@ 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, method-handles} +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 diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index a7e7c21a42..9ebf9a7bdd 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -18,8 +18,9 @@ #include <stdint.h> +#include "art_method.h" #include "indenter.h" -#include "invoke_type.h" +#include "scoped_thread_state_change-inl.h" namespace art { @@ -106,7 +107,7 @@ void InlineInfoEncoding::Dump(VariableIndentationOutputStream* vios) const { << "InlineInfoEncoding" << " (method_index_bit_offset=" << static_cast<uint32_t>(kMethodIndexBitOffset) << ", dex_pc_bit_offset=" << static_cast<uint32_t>(dex_pc_bit_offset_) - << ", invoke_type_bit_offset=" << static_cast<uint32_t>(invoke_type_bit_offset_) + << ", extra_data_bit_offset=" << static_cast<uint32_t>(extra_data_bit_offset_) << ", dex_register_map_bit_offset=" << static_cast<uint32_t>(dex_register_map_bit_offset_) << ", total_bit_size=" << static_cast<uint32_t>(total_bit_size_) << ")\n"; @@ -230,12 +231,16 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, vios->Stream() << " At depth " << i << std::hex - << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i) - << std::dec - << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i) - << ", invoke_type=" << static_cast<InvokeType>(GetInvokeTypeAtDepth(inline_info_encoding, - i)) - << ")\n"; + << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i); + if (EncodesArtMethodAtDepth(inline_info_encoding, i)) { + ScopedObjectAccess soa(Thread::Current()); + vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod(); + } else { + vios->Stream() + << std::dec + << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i); + } + vios->Stream() << ")\n"; if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) { CodeInfoEncoding encoding = code_info.ExtractEncoding(); DexRegisterMap dex_register_map = diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 5e556be286..13886f2109 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -35,6 +35,7 @@ static constexpr ssize_t kFrameSlotSize = 4; // Size of Dex virtual registers. static constexpr size_t kVRegSize = 4; +class ArtMethod; class CodeInfo; class StackMapEncoding; struct CodeInfoEncoding; @@ -887,7 +888,7 @@ class InlineInfoEncoding { public: void SetFromSizes(size_t method_index_max, size_t dex_pc_max, - size_t invoke_type_max, + size_t extra_data_max, size_t dex_register_map_size) { total_bit_size_ = kMethodIndexBitOffset; total_bit_size_ += MinimumBitsToStore(method_index_max); @@ -899,8 +900,8 @@ class InlineInfoEncoding { total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); } - invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(invoke_type_max); + extra_data_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); + total_bit_size_ += MinimumBitsToStore(extra_data_max); // We also need +1 for kNoDexRegisterMap, but since the size is strictly // greater than any offset we might try to encode, we already implicitly have it. @@ -912,10 +913,10 @@ class InlineInfoEncoding { return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_); } ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const { - return FieldEncoding(dex_pc_bit_offset_, invoke_type_bit_offset_, -1 /* min_value */); + return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */); } - ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const { - return FieldEncoding(invoke_type_bit_offset_, dex_register_map_bit_offset_); + ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const { + return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_); } ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const { return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */); @@ -930,7 +931,7 @@ class InlineInfoEncoding { static constexpr uint8_t kIsLastBitOffset = 0; static constexpr uint8_t kMethodIndexBitOffset = 1; uint8_t dex_pc_bit_offset_; - uint8_t invoke_type_bit_offset_; + uint8_t extra_data_bit_offset_; uint8_t dex_register_map_bit_offset_; uint8_t total_bit_size_; }; @@ -938,7 +939,11 @@ class InlineInfoEncoding { /** * Inline information for a specific PC. The information is of the form: * - * [is_last, method_index, dex_pc, invoke_type, dex_register_map_offset]+. + * [is_last, + * method_index (or ArtMethod high bits), + * dex_pc, + * extra_data (ArtMethod low bits or 1), + * dex_register_map_offset]+. */ class InlineInfo { public: @@ -960,6 +965,7 @@ class InlineInfo { ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding, uint32_t depth) const { + DCHECK(!EncodesArtMethodAtDepth(encoding, depth)); return encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth)); } @@ -980,15 +986,28 @@ class InlineInfo { encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc); } - ALWAYS_INLINE uint32_t GetInvokeTypeAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return encoding.GetInvokeTypeEncoding().Load(GetRegionAtDepth(encoding, depth)); + ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding, + uint32_t depth) const { + return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0; + } + + ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding, + uint32_t depth, + uint32_t extra_data) { + encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data); } - ALWAYS_INLINE void SetInvokeTypeAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t invoke_type) { - encoding.GetInvokeTypeEncoding().Store(GetRegionAtDepth(encoding, depth), invoke_type); + ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding, + uint32_t depth) const { + uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)); + uint32_t high_bits = encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth)); + if (high_bits == 0) { + return reinterpret_cast<ArtMethod*>(low_bits); + } else { + uint64_t address = high_bits; + address = address << 32; + return reinterpret_cast<ArtMethod*>(address | low_bits); + } } ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding, @@ -1108,7 +1127,7 @@ class CodeInfo { GetDexRegisterLocationCatalogSize(encoding))); } - StackMap GetStackMapAt(size_t i, const CodeInfoEncoding& encoding) const { + ALWAYS_INLINE StackMap GetStackMapAt(size_t i, const CodeInfoEncoding& encoding) const { size_t stack_map_size = encoding.stack_map_size_in_bytes; return StackMap(GetStackMaps(encoding).Subregion(i * stack_map_size, stack_map_size)); } @@ -1269,7 +1288,7 @@ class CodeInfo { } private: - MemoryRegion GetStackMaps(const CodeInfoEncoding& encoding) const { + ALWAYS_INLINE MemoryRegion GetStackMaps(const CodeInfoEncoding& encoding) const { return region_.size() == 0 ? MemoryRegion() : region_.Subregion(GetStackMapsOffset(encoding), GetStackMapsSize(encoding)); diff --git a/runtime/thread.cc b/runtime/thread.cc index bdd4ca6721..d93eab10a0 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -67,6 +67,7 @@ #include "quick/quick_method_frame_info.h" #include "reflection.h" #include "runtime.h" +#include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" @@ -431,7 +432,8 @@ void* Thread::CreateCallback(void* arg) { ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority); self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer)); - Dbg::PostThreadStart(self); + + runtime->GetRuntimeCallbacks()->ThreadStart(self); // Invoke the 'run' method of our java.lang.Thread. ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer; @@ -723,8 +725,8 @@ bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_en return true; } -Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group, - bool create_peer) { +template <typename PeerAction> +Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) { Runtime* runtime = Runtime::Current(); if (runtime == nullptr) { LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name; @@ -753,32 +755,11 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g CHECK_NE(self->GetState(), kRunnable); self->SetState(kNative); - // If we're the main thread, ClassLinker won't be created until after we're attached, - // so that thread needs a two-stage attach. Regular threads don't need this hack. - // In the compiler, all threads need this hack, because no-one's going to be getting - // a native peer! - if (create_peer) { - self->CreatePeer(thread_name, as_daemon, thread_group); - if (self->IsExceptionPending()) { - // We cannot keep the exception around, as we're deleting self. Try to be helpful and log it. - { - ScopedObjectAccess soa(self); - LOG(ERROR) << "Exception creating thread peer:"; - LOG(ERROR) << self->GetException()->Dump(); - self->ClearException(); - } - runtime->GetThreadList()->Unregister(self); - // Unregister deletes self, no need to do this here. - return nullptr; - } - } else { - // These aren't necessary, but they improve diagnostics for unit tests & command-line tools. - if (thread_name != nullptr) { - self->tlsPtr_.name->assign(thread_name); - ::art::SetThreadName(thread_name); - } else if (self->GetJniEnv()->check_jni) { - LOG(WARNING) << *Thread::Current() << " attached without supplying a name"; - } + // Run the action that is acting on the peer. + if (!peer_action(self)) { + runtime->GetThreadList()->Unregister(self); + // Unregister deletes self, no need to do this here. + return nullptr; } if (VLOG_IS_ON(threads)) { @@ -793,12 +774,63 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g { ScopedObjectAccess soa(self); - Dbg::PostThreadStart(self); + runtime->GetRuntimeCallbacks()->ThreadStart(self); } return self; } +Thread* Thread::Attach(const char* thread_name, + bool as_daemon, + jobject thread_group, + bool create_peer) { + auto create_peer_action = [&](Thread* self) { + // If we're the main thread, ClassLinker won't be created until after we're attached, + // so that thread needs a two-stage attach. Regular threads don't need this hack. + // In the compiler, all threads need this hack, because no-one's going to be getting + // a native peer! + if (create_peer) { + self->CreatePeer(thread_name, as_daemon, thread_group); + if (self->IsExceptionPending()) { + // We cannot keep the exception around, as we're deleting self. Try to be helpful and log it. + { + ScopedObjectAccess soa(self); + LOG(ERROR) << "Exception creating thread peer:"; + LOG(ERROR) << self->GetException()->Dump(); + self->ClearException(); + } + return false; + } + } else { + // These aren't necessary, but they improve diagnostics for unit tests & command-line tools. + if (thread_name != nullptr) { + self->tlsPtr_.name->assign(thread_name); + ::art::SetThreadName(thread_name); + } else if (self->GetJniEnv()->check_jni) { + LOG(WARNING) << *Thread::Current() << " attached without supplying a name"; + } + } + return true; + }; + return Attach(thread_name, as_daemon, create_peer_action); +} + +Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_peer) { + auto set_peer_action = [&](Thread* self) { + // Install the given peer. + { + DCHECK(self == Thread::Current()); + ScopedObjectAccess soa(self); + self->tlsPtr_.opeer = soa.Decode<mirror::Object>(thread_peer).Ptr(); + } + self->GetJniEnv()->SetLongField(thread_peer, + WellKnownClasses::java_lang_Thread_nativePeer, + reinterpret_cast<jlong>(self)); + return true; + }; + return Attach(thread_name, as_daemon, set_peer_action); +} + void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) { Runtime* runtime = Runtime::Current(); CHECK(runtime->IsStarted()); @@ -1929,7 +1961,11 @@ void Thread::Destroy() { jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer) ->SetLong<false>(tlsPtr_.opeer, 0); } - Dbg::PostThreadDeath(self); + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + runtime->GetRuntimeCallbacks()->ThreadDeath(self); + } + // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone // who is waiting. @@ -2626,14 +2662,10 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { os << #x; \ return; \ } - QUICK_ENTRY_POINT_INFO(pAllocArray) QUICK_ENTRY_POINT_INFO(pAllocArrayResolved) - QUICK_ENTRY_POINT_INFO(pAllocArrayWithAccessCheck) QUICK_ENTRY_POINT_INFO(pAllocObjectResolved) QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized) QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks) - QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray) - QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck) QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes) QUICK_ENTRY_POINT_INFO(pAllocStringFromChars) QUICK_ENTRY_POINT_INFO(pAllocStringFromString) @@ -2667,10 +2699,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pGet64Static) QUICK_ENTRY_POINT_INFO(pGetObjInstance) QUICK_ENTRY_POINT_INFO(pGetObjStatic) - QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck) - QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck) QUICK_ENTRY_POINT_INFO(pAputObject) - QUICK_ENTRY_POINT_INFO(pHandleFillArrayData) QUICK_ENTRY_POINT_INFO(pJniMethodStart) QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized) QUICK_ENTRY_POINT_INFO(pJniMethodEnd) @@ -2727,6 +2756,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pInvokeStaticTrampolineWithAccessCheck) QUICK_ENTRY_POINT_INFO(pInvokeSuperTrampolineWithAccessCheck) QUICK_ENTRY_POINT_INFO(pInvokeVirtualTrampolineWithAccessCheck) + QUICK_ENTRY_POINT_INFO(pInvokePolymorphic) QUICK_ENTRY_POINT_INFO(pTestSuspend) QUICK_ENTRY_POINT_INFO(pDeliverException) QUICK_ENTRY_POINT_INFO(pThrowArrayBounds) diff --git a/runtime/thread.h b/runtime/thread.h index a3ef9bc0a3..b609e723e9 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -158,6 +158,8 @@ class Thread { // Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls. static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_group, bool create_peer); + // Attaches the calling native thread to the runtime, returning the new native peer. + static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_peer); // Reset internal state of child thread after fork. void InitAfterFork(); @@ -1140,6 +1142,14 @@ class Thread { return debug_disallow_read_barrier_; } + const void* GetCustomTLS() const { + return custom_tls_; + } + + void SetCustomTLS(const void* data) { + custom_tls_ = data; + } + // Returns true if the current thread is the jit sensitive thread. bool IsJitSensitiveThread() const { return this == jit_sensitive_thread_; @@ -1158,6 +1168,13 @@ class Thread { ~Thread() REQUIRES(!Locks::mutator_lock_, !Locks::thread_suspend_count_lock_); void Destroy(); + // Attaches the calling native thread to the runtime, returning the new native peer. + // Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls. + template <typename PeerAction> + static Thread* Attach(const char* thread_name, + bool as_daemon, + PeerAction p); + void CreatePeer(const char* name, bool as_daemon, jobject thread_group); template<bool kTransactionActive> @@ -1537,13 +1554,9 @@ class Thread { // to avoid additional cost of a mutex and a condition variable, as used in art::Barrier. AtomicInteger* active_suspend_barriers[kMaxSuspendBarriers]; - // Entrypoint function pointers. - // TODO: move this to more of a global offset table model to avoid per-thread duplication. - JniEntryPoints jni_entrypoints; - QuickEntryPoints quick_entrypoints; - // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM. uint8_t* thread_local_start; + // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for // potentially better performance. uint8_t* thread_local_pos; @@ -1551,6 +1564,11 @@ class Thread { size_t thread_local_objects; + // Entrypoint function pointers. + // TODO: move this to more of a global offset table model to avoid per-thread duplication. + JniEntryPoints jni_entrypoints; + QuickEntryPoints quick_entrypoints; + // Mterp jump table bases. void* mterp_current_ibase; void* mterp_default_ibase; @@ -1599,6 +1617,10 @@ class Thread { // Pending extra checkpoints if checkpoint_function_ is already used. std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_); + // Custom TLS field that can be used by plugins. + // TODO: Generalize once we have more plugins. + const void* custom_tls_; + // True if the thread is allowed to call back into java (for e.g. during class resolution). // By default this is true. bool can_call_into_java_; @@ -1691,6 +1713,14 @@ class ScopedTransitioningToRunnable : public ValueObject { Thread* const self_; }; +class ThreadLifecycleCallback { + public: + virtual ~ThreadLifecycleCallback() {} + + virtual void ThreadStart(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void ThreadDeath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + std::ostream& operator<<(std::ostream& os, const Thread& thread); std::ostream& operator<<(std::ostream& os, const StackedShadowFrameType& thread); diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 3b114a9f10..bb9844af65 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -61,7 +61,7 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - static constexpr uint8_t kVdexVersion[] = { '0', '0', '1', '\0' }; + static constexpr uint8_t kVdexVersion[] = { '0', '0', '2', '\0' }; // Handle verify-profile uint8_t magic_[4]; uint8_t version_[4]; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 715b237a6a..b915457557 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2901,9 +2901,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range); const RegType* return_type = nullptr; if (called_method != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type = &FromClass(called_method->GetReturnTypeDescriptor(), return_type_class, @@ -2946,9 +2944,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { is_constructor = called_method->IsConstructor(); return_type_descriptor = called_method->GetReturnTypeDescriptor(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type = &FromClass(return_type_descriptor, return_type_class, @@ -3106,19 +3102,16 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); - const char* descriptor = + const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); const RegType& return_type = - reg_types_.FromDescriptor(GetClassLoader(), descriptor, false); + reg_types_.FromDescriptor(GetClassLoader(), return_descriptor, false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); } - // TODO(oth): remove when compiler support is available. - Fail(VERIFY_ERROR_FORCE_INTERPRETER) - << "invoke-polymorphic is not supported by compiler"; - have_pending_experimental_failure_ = true; + just_set_result = true; break; } case Instruction::NEG_INT: @@ -5136,9 +5129,7 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { const RegType& MethodVerifier::GetMethodReturnType() { if (return_type_ == nullptr) { if (mirror_method_ != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), return_type_class, diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 15cc566cc6..113160785c 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -963,20 +963,25 @@ bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader, // Check recorded fields are resolved the same way, have the same recorded class, // and have the same recorded flags. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache( - hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false))); for (const auto& entry : fields) { - ArtField* field = class_linker->ResolveFieldJLS( - dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader); - - if (field == nullptr) { - DCHECK(self->IsExceptionPending()); - self->ClearException(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex()); + StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_)); + StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_)); + // Only use field_id.class_idx_ when the entry is unresolved, which is rare. + // Otherwise, we might end up resolving an application class, which is expensive. + std::string expected_decl_klass = entry.IsResolved() + ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) + : dex_file.StringByTypeIdx(field_id.class_idx_); + mirror::Class* cls = FindClassAndClearException( + class_linker, self, expected_decl_klass.c_str(), class_loader); + if (cls == nullptr) { + LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; + return false; } + DCHECK(cls->IsResolved()); + ArtField* field = mirror::Class::FindField(self, cls, name, type); if (entry.IsResolved()) { - std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex()); std::string temp; if (field == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve field " @@ -1025,11 +1030,16 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, const char* name = dex_file.GetMethodName(method_id); const Signature signature = dex_file.GetMethodSignature(method_id); - const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id); - - mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader); + // Only use method_id.class_idx_ when the entry is unresolved, which is rare. + // Otherwise, we might end up resolving an application class, which is expensive. + std::string expected_decl_klass = entry.IsResolved() + ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex()) + : dex_file.StringByTypeIdx(method_id.class_idx_); + + mirror::Class* cls = FindClassAndClearException( + class_linker, self, expected_decl_klass.c_str(), class_loader); if (cls == nullptr) { - LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor; + LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass; return false; } DCHECK(cls->IsResolved()); @@ -1045,7 +1055,6 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, if (entry.IsResolved()) { std::string temp; - std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex()); if (method == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve " << kind diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index a5b275cc3b..507ea165e5 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -103,6 +103,7 @@ jmethodID WellKnownClasses::java_lang_reflect_Parameter_init; jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke; jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad; jmethodID WellKnownClasses::java_lang_Short_valueOf; +jmethodID WellKnownClasses::java_lang_String_charAt; jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr; jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException; jmethodID WellKnownClasses::java_lang_Thread_init; @@ -337,6 +338,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V"); java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V"); + java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C"); java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V"); java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 371be61f43..b3ce3d1597 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -113,6 +113,7 @@ struct WellKnownClasses { static jmethodID java_lang_reflect_Proxy_invoke; static jmethodID java_lang_Runtime_nativeLoad; static jmethodID java_lang_Short_valueOf; + static jmethodID java_lang_String_charAt; static jmethodID java_lang_System_runFinalization; static jmethodID java_lang_Thread_dispatchUncaughtException; static jmethodID java_lang_Thread_init; diff --git a/test/080-oom-throw/expected.txt b/test/080-oom-throw/expected.txt index 904393bc3b..0967278314 100644 --- a/test/080-oom-throw/expected.txt +++ b/test/080-oom-throw/expected.txt @@ -1,3 +1,4 @@ Test reflection correctly threw +Test reflection2 correctly threw NEW_ARRAY correctly threw OOME NEW_INSTANCE correctly threw OOME diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java index 0ae92a96dc..a6c18b75fc 100644 --- a/test/080-oom-throw/src/Main.java +++ b/test/080-oom-throw/src/Main.java @@ -53,6 +53,30 @@ public class Main { } } + public static Object eatAllMemory() { + Object[] result = null; + int size = 1000000; + while (result == null && size != 0) { + try { + result = new Object[size]; + } catch (OutOfMemoryError oome) { + size /= 2; + } + } + if (result != null) { + int index = 0; + while (index != result.length && size != 0) { + try { + result[index] = new byte[size]; + ++index; + } catch (OutOfMemoryError oome) { + size /= 2; + } + } + } + return result; + } + static boolean triggerArrayOOM() { ArrayMemEater.blowup(new char[128 * 1024][]); return ArrayMemEater.sawOome; @@ -74,6 +98,9 @@ public class Main { if (triggerReflectionOOM()) { System.out.println("Test reflection correctly threw"); } + if (triggerReflectionOOM2()) { + System.out.println("Test reflection2 correctly threw"); + } if (triggerArrayOOM()) { System.out.println("NEW_ARRAY correctly threw OOME"); @@ -125,4 +152,20 @@ public class Main { } return true; } + + static boolean triggerReflectionOOM2() { + Object memory = eatAllMemory(); + boolean result = false; + try { + Main.class.getDeclaredMethods(); + } catch (OutOfMemoryError e) { + result = true; + } + if (!result) { + boolean memoryWasAllocated = (memory != null); + memory = null; + System.out.println("memoryWasAllocated = " + memoryWasAllocated); + } + return result; + } } diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java index 5aefd17f0e..4e48e0e1a6 100644 --- a/test/129-ThreadGetId/src/Main.java +++ b/test/129-ThreadGetId/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.lang.reflect.Field; import java.util.Map; public class Main implements Runnable { @@ -22,7 +23,6 @@ public class Main implements Runnable { public static void main(String[] args) throws Exception { final Thread[] threads = new Thread[numberOfThreads]; - test_getStackTraces(); for (int t = 0; t < threads.length; t++) { threads[t] = new Thread(new Main()); threads[t].start(); @@ -30,17 +30,44 @@ public class Main implements Runnable { for (Thread t : threads) { t.join(); } + // Do this test after the other part to leave some time for the heap task daemon to start + // up. + test_getStackTraces(); System.out.println("Finishing"); } - static void test_getStackTraces() { + static Thread getHeapTaskDaemon() throws Exception { + Field f = ThreadGroup.class.getDeclaredField("systemThreadGroup"); + f.setAccessible(true); + ThreadGroup systemThreadGroup = (ThreadGroup) f.get(null); + + while (true) { + int activeCount = systemThreadGroup.activeCount(); + Thread[] array = new Thread[activeCount]; + systemThreadGroup.enumerate(array); + for (Thread thread : array) { + if (thread.getName().equals("HeapTaskDaemon") && + thread.getState() != Thread.State.NEW) { + return thread; + } + } + // Yield to eventually get the daemon started. + Thread.sleep(10); + } + } + + static void test_getStackTraces() throws Exception { + Thread heapDaemon = getHeapTaskDaemon(); + + // Force a GC to ensure the daemon truly started. + Runtime.getRuntime().gc(); // Check all the current threads for positive IDs. Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); for (Map.Entry<Thread, StackTraceElement[]> pair : map.entrySet()) { Thread thread = pair.getKey(); // Expect empty stack trace since we do not support suspending the GC thread for // obtaining stack traces. See b/28261069. - if (thread.getName().equals("HeapTaskDaemon")) { + if (thread == heapDaemon) { System.out.println(thread.getName() + " depth " + pair.getValue().length); } } diff --git a/test/494-checker-instanceof-tests/src/Main.java b/test/494-checker-instanceof-tests/src/Main.java index 2eac6c92a5..acd2305f67 100644 --- a/test/494-checker-instanceof-tests/src/Main.java +++ b/test/494-checker-instanceof-tests/src/Main.java @@ -142,11 +142,11 @@ public class Main { /// CHECK: LoadClass /// CHECK: Return [<<Const>>] public static boolean knownTestWithUnloadedClass() { - return $inline$returnMain() instanceof String; + return $inline$returnUnrelated() instanceof String; } - public static Object $inline$returnMain() { - return new Main(); + public static Object $inline$returnUnrelated() { + return new Unrelated(); } public static void expect(boolean expected, boolean actual) { diff --git a/test/496-checker-inlining-class-loader/src/Main.java b/test/496-checker-inlining-class-loader/src/Main.java index 15d4dc07bc..5deb77f29e 100644 --- a/test/496-checker-inlining-class-loader/src/Main.java +++ b/test/496-checker-inlining-class-loader/src/Main.java @@ -82,10 +82,10 @@ class MyClassLoader extends ClassLoader { class LoadedByMyClassLoader { /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before) - /// CHECK: LoadClass + /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader /// CHECK-NEXT: ClinitCheck /// CHECK-NEXT: InvokeStaticOrDirect - /// CHECK-NEXT: LoadClass + /// CHECK-NEXT: LoadClass class_name:java.lang.System /// CHECK-NEXT: ClinitCheck /// CHECK-NEXT: StaticFieldGet /// CHECK-NEXT: LoadString @@ -93,10 +93,10 @@ class LoadedByMyClassLoader { /// CHECK-NEXT: InvokeVirtual /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after) - /// CHECK: LoadClass + /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader /// CHECK-NEXT: ClinitCheck /* We inlined FirstSeenByMyClassLoader.$inline$bar */ - /// CHECK-NEXT: LoadClass + /// CHECK-NEXT: LoadClass class_name:java.lang.System /// CHECK-NEXT: ClinitCheck /// CHECK-NEXT: StaticFieldGet /// CHECK-NEXT: LoadString @@ -105,12 +105,15 @@ class LoadedByMyClassLoader { /// CHECK-START: void LoadedByMyClassLoader.bar() register (before) /* Load and initialize FirstSeenByMyClassLoader */ - /// CHECK: LoadClass gen_clinit_check:true + /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true /* Load and initialize System */ // There may be MipsComputeBaseMethodAddress here. - /// CHECK: LoadClass gen_clinit_check:true - /// CHECK-NEXT: StaticFieldGet - // There may be HArmDexCacheArraysBase or HX86ComputeBaseMethodAddress here. + /// CHECK: LoadClass class_name:java.lang.System + // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass. + // (The merging checks for environment match but HLoadClass/kBootImageAddress + // used for non-PIC mode does not have an environment at all.) + /// CHECK: StaticFieldGet + // There may be HX86ComputeBaseMethodAddress or MipsComputeBaseMethodAddress here. /// CHECK: LoadString /// CHECK-NEXT: NullCheck /// CHECK-NEXT: InvokeVirtual diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index fe6ff13628..db437686f0 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -331,32 +331,32 @@ public class Main { /// CHECK-START-X86: java.lang.Class Main.$noinline$getStringClass() sharpening (after) // Note: load kind depends on PIC/non-PIC // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. - /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} class_name:java.lang.String /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) // Note: load kind depends on PIC/non-PIC // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. - /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} class_name:java.lang.String /// CHECK-START-ARM: java.lang.Class Main.$noinline$getStringClass() sharpening (after) // Note: load kind depends on PIC/non-PIC // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. - /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} class_name:java.lang.String /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) // Note: load kind depends on PIC/non-PIC // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. - /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} class_name:java.lang.String /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getStringClass() sharpening (after) // Note: load kind depends on PIC/non-PIC // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. - /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} class_name:java.lang.String /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) // Note: load kind depends on PIC/non-PIC // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. - /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK: LoadClass load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} class_name:java.lang.String public static Class<?> $noinline$getStringClass() { // Prevent inlining to avoid the string comparison being optimized away. @@ -369,34 +369,34 @@ public class Main { /// CHECK: LoadClass load_kind:DexCacheViaMethod class_name:Other /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (after) /// CHECK-DAG: X86ComputeBaseMethodAddress - /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_arm (after) /// CHECK-DAG: ArmDexCacheArraysBase - /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_mips (after) /// CHECK-DAG: MipsDexCacheArraysBase - /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK: LoadClass load_kind:BssEntry class_name:Other public static Class<?> $noinline$getOtherClass() { // Prevent inlining to avoid the string comparison being optimized away. diff --git a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali index af43973073..a30a11afb7 100644 --- a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali +++ b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali @@ -196,7 +196,7 @@ const-class v0, LMain; if-ne v0, v2, :exit :other_loop_entry - const-class v1, Ljava/lang/Class; # LoadClass that can throw + const-class v1, LOther; # LoadClass that can throw goto :loop_entry :exit return-object v0 @@ -250,7 +250,7 @@ const/4 v0, 0 if-ne p0, v0, :other_loop_entry :loop_entry - const-class v1, Ljava/lang/Class; # LoadClass that can throw + const-class v1, LOther; # LoadClass that can throw if-ne v0, p0, :exit :other_loop_entry sub-int v1, p0, p0 @@ -286,7 +286,7 @@ .method public static licm3(III)I .registers 4 :loop_entry - const-class v0, Ljava/lang/Class; # LoadClass that can throw + const-class v0, LOther; # LoadClass that can throw if-ne p1, p2, :exit goto :loop_body diff --git a/test/559-checker-irreducible-loop/src/Main.java b/test/559-checker-irreducible-loop/src/Main.java index ab84f81291..023e7695fe 100644 --- a/test/559-checker-irreducible-loop/src/Main.java +++ b/test/559-checker-irreducible-loop/src/Main.java @@ -67,3 +67,6 @@ public class Main { int myField; } + +class Other { +} diff --git a/test/559-checker-rtp-ifnotnull/src/Main.java b/test/559-checker-rtp-ifnotnull/src/Main.java index 2dc5666e18..1e15654950 100644 --- a/test/559-checker-rtp-ifnotnull/src/Main.java +++ b/test/559-checker-rtp-ifnotnull/src/Main.java @@ -18,7 +18,6 @@ public class Main { /// CHECK-START: void Main.boundTypeForIfNotNull() builder (after) - /// CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod /// CHECK-DAG: <<Null:l\d+>> NullConstant /// CHECK-DAG: <<Cst5:i\d+>> IntConstant 5 /// CHECK-DAG: <<Cst10:i\d+>> IntConstant 10 @@ -28,10 +27,12 @@ public class Main { /// CHECK-DAG: <<LoopPhi>> Phi [<<Null>>,<<MergePhi:l\d+>>] klass:int[] /// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<LoopPhi>>] klass:int[] can_be_null:false - /// CHECK-DAG: <<NewArray10:l\d+>> NewArray [<<Cst10>>,<<Method>>] klass:int[] + /// CHECK-DAG: <<LoadClass1:l\d+>> LoadClass + /// CHECK-DAG: <<LoadClass2:l\d+>> LoadClass + /// CHECK-DAG: <<NewArray10:l\d+>> NewArray [<<LoadClass2>>,<<Cst10>>] klass:int[] /// CHECK-DAG: <<NotNullPhi:l\d+>> Phi [<<BoundType>>,<<NewArray10>>] klass:int[] - /// CHECK-DAG: <<NewArray5:l\d+>> NewArray [<<Cst5>>,<<Method>>] klass:int[] + /// CHECK-DAG: <<NewArray5:l\d+>> NewArray [<<LoadClass1>>,<<Cst5>>] klass:int[] /// CHECK-DAG: <<MergePhi>> Phi [<<NewArray5>>,<<NotNullPhi>>] klass:int[] public static void boundTypeForIfNotNull() { diff --git a/test/572-checker-array-get-regression/src/Main.java b/test/572-checker-array-get-regression/src/Main.java index 89b97ed316..03a84486de 100644 --- a/test/572-checker-array-get-regression/src/Main.java +++ b/test/572-checker-array-get-regression/src/Main.java @@ -21,10 +21,10 @@ public class Main { } /// CHECK-START: java.lang.Integer Main.test() builder (after) - /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod /// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288 /// CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 - /// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>] + /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass + /// CHECK-DAG: <<Array:l\d+>> NewArray [<<LoadClass>>,<<Const2P19>>] /// CHECK-DAG: <<Length1:i\d+>> ArrayLength [<<Array>>] /// CHECK-DAG: <<Index:i\d+>> Add [<<Length1>>,<<ConstM1>>] /// CHECK-DAG: <<Length2:i\d+>> ArrayLength [<<Array>>] @@ -34,10 +34,10 @@ public class Main { /// CHECK-START: java.lang.Integer Main.test() register (before) - /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod /// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288 /// CHECK-DAG: <<Const2P19M1:i\d+>> IntConstant 524287 - /// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>] + /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass + /// CHECK-DAG: <<Array:l\d+>> NewArray [<<LoadClass>>,<<Const2P19>>] /// CHECK-DAG: <<LastElement:l\d+>> ArrayGet [<<Array>>,<<Const2P19M1>>] /// CHECK-DAG: Return [<<LastElement>>] diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc index bf3d812f94..0f8dd57385 100644 --- a/test/595-profile-saving/profile-saving.cc +++ b/test/595-profile-saving/profile-saving.cc @@ -17,7 +17,7 @@ #include "dex_file.h" #include "art_method-inl.h" -#include "jit/offline_profiling_info.h" +#include "jit/profile_compilation_info.h" #include "jit/profile_saver.h" #include "jni.h" #include "method_reference.h" diff --git a/test/627-checker-unroll/expected.txt b/test/627-checker-unroll/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/627-checker-unroll/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/627-checker-unroll/info.txt b/test/627-checker-unroll/info.txt new file mode 100644 index 0000000000..d7885f4bbc --- /dev/null +++ b/test/627-checker-unroll/info.txt @@ -0,0 +1 @@ +Test on loop unrolling. diff --git a/test/627-checker-unroll/src/Main.java b/test/627-checker-unroll/src/Main.java new file mode 100644 index 0000000000..9785bdc58b --- /dev/null +++ b/test/627-checker-unroll/src/Main.java @@ -0,0 +1,119 @@ +/* + * 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. + */ + +// +// Test on loop unrolling. Removes loop control overhead (including suspend +// checks) and exposes more opportunities for constant folding. +// +public class Main { + + static int sA = 0; + + /// CHECK-START: void Main.unroll() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: StaticFieldSet loop:<<Loop>> + // + /// CHECK-START: void Main.unroll() loop_optimization (after) + /// CHECK-DAG: StaticFieldSet loop:none + // + /// CHECK-START: void Main.unroll() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant 68 loop:none + /// CHECK-DAG: StaticFieldSet [{{l\d+}},<<Int>>] loop:none + // + /// CHECK-START: void Main.unroll() loop_optimization (after) + /// CHECK-NOT: Phi + public static void unroll() { + for (int i = 4; i < 5; i++) { + sA = 17 * i; + } + } + + /// CHECK-START: int Main.unrollLV() loop_optimization (before) + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: StaticFieldSet loop:<<Loop>> + /// CHECK-DAG: Return [<<Phi>>] loop:none + // + /// CHECK-START: int Main.unrollLV() loop_optimization (after) + /// CHECK-DAG: StaticFieldSet loop:none + // + /// CHECK-START: int Main.unrollLV() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int1:i\d+>> IntConstant 187 loop:none + /// CHECK-DAG: <<Int2:i\d+>> IntConstant 12 loop:none + /// CHECK-DAG: StaticFieldSet [{{l\d+}},<<Int1>>] loop:none + /// CHECK-DAG: Return [<<Int2>>] loop:none + // + /// CHECK-START: int Main.unrollLV() loop_optimization (after) + /// CHECK-NOT: Phi + public static int unrollLV() { + int i; + for (i = 11; i < 12; i++) { + sA = 17 * i; + } + return i; + } + + /// CHECK-START: void Main.unrollNest() loop_optimization (before) + /// CHECK-DAG: SuspendCheck loop:none + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none + /// CHECK-DAG: SuspendCheck loop:<<Loop1>> outer_loop:none + /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>> + /// CHECK-DAG: SuspendCheck loop:<<Loop2>> outer_loop:<<Loop1>> + /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop3:B\d+>> outer_loop:<<Loop2>> + /// CHECK-DAG: SuspendCheck loop:<<Loop3>> outer_loop:<<Loop2>> + /// CHECK-DAG: StaticFieldSet loop:<<Loop3>> outer_loop:<<Loop2>> + // + /// CHECK-START: void Main.unrollNest() loop_optimization (after) + /// CHECK-DAG: StaticFieldSet loop:none + /// CHECK-DAG: SuspendCheck loop:none + /// CHECK-NOT: SuspendCheck + // + /// CHECK-START: void Main.unrollNest() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant 6 loop:none + /// CHECK-DAG: StaticFieldSet [{{l\d+}},<<Int>>] loop:none + // + /// CHECK-START: void Main.unrollNest() loop_optimization (after) + /// CHECK-NOT: Phi + public static void unrollNest() { + // Unrolling each loop in turn ultimately removes the complete nest! + for (int i = 4; i < 5; i++) { + for (int j = 5; j < 6; j++) { + for (int k = 6; k < 7; k++) { + sA = k; + } + } + } + } + + // + // Verifier. + // + + public static void main(String[] args) { + unroll(); + expectEquals(68, sA); + expectEquals(12, unrollLV()); + expectEquals(187, sA); + unrollNest(); + expectEquals(6, sA); + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/633-checker-rtp-getclass/expected.txt b/test/633-checker-rtp-getclass/expected.txt new file mode 100644 index 0000000000..a178d0414b --- /dev/null +++ b/test/633-checker-rtp-getclass/expected.txt @@ -0,0 +1,3 @@ +2 +3 +6 diff --git a/test/633-checker-rtp-getclass/info.txt b/test/633-checker-rtp-getclass/info.txt new file mode 100644 index 0000000000..e98a0acbbb --- /dev/null +++ b/test/633-checker-rtp-getclass/info.txt @@ -0,0 +1,3 @@ +Regression test for the RTP pass of the compiler, which +used the wrong block when bounding a type after a obj.getClass() +check. diff --git a/test/633-checker-rtp-getclass/src/Main.java b/test/633-checker-rtp-getclass/src/Main.java new file mode 100644 index 0000000000..f29c139f63 --- /dev/null +++ b/test/633-checker-rtp-getclass/src/Main.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +public class Main { + public static void main(String[] args) { + System.out.println($opt$noinline$foo(new Main())); + System.out.println($opt$noinline$foo(new SubMain())); + System.out.println($opt$noinline$foo(new SubSubMain())); + } + + + // Checker test to make sure the only inlined instruction is + // SubMain.bar. + /// CHECK-START: int Main.$opt$noinline$foo(Main) inliner (after) + /// CHECK-DAG: InvokeVirtual method_name:Main.foo + /// CHECK-DAG: <<Const:i\d+>> IntConstant 3 + /// CHECK: begin_block + /// CHECK: BoundType klass:SubMain + /// CHECK: Return [<<Const>>] + /// CHECK-NOT: begin_block + /// CHECK: end_block + public static int $opt$noinline$foo(Main o) { + if (doThrow) { throw new Error(); } + // To exercise the bug on Jack, we need two getClass compares. + if (o.getClass() == Main.class || o.getClass() != SubMain.class) { + return o.foo(); + } else { + // We used to wrongly bound the type of o to `Main` here and then realize that's + // impossible and mark this branch as dead. + return o.bar(); + } + } + + public int bar() { + return 1; + } + + public int foo() { + return 2; + } + + public static boolean doThrow = false; +} + +class SubMain extends Main { + public int bar() { + return 3; + } + + public int foo() { + return 4; + } +} + +class SubSubMain extends SubMain { + public int bar() { + return 5; + } + + public int foo() { + return 6; + } +} diff --git a/test/634-vdex-duplicate/expected.txt b/test/634-vdex-duplicate/expected.txt new file mode 100644 index 0000000000..557db03de9 --- /dev/null +++ b/test/634-vdex-duplicate/expected.txt @@ -0,0 +1 @@ +Hello World diff --git a/test/634-vdex-duplicate/info.txt b/test/634-vdex-duplicate/info.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/634-vdex-duplicate/info.txt diff --git a/test/634-vdex-duplicate/run b/test/634-vdex-duplicate/run new file mode 100644 index 0000000000..1ccb84150b --- /dev/null +++ b/test/634-vdex-duplicate/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# 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. + +exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex-filter speed --vdex "${@}" diff --git a/test/634-vdex-duplicate/src/Main.java b/test/634-vdex-duplicate/src/Main.java new file mode 100644 index 0000000000..228310697a --- /dev/null +++ b/test/634-vdex-duplicate/src/Main.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. + */ + +public class Main { + public static void main(String[] args) { + System.out.println("Hello World"); + } +} diff --git a/test/634-vdex-duplicate/src/sun/misc/Unsafe.java b/test/634-vdex-duplicate/src/sun/misc/Unsafe.java new file mode 100644 index 0000000000..c32868cf37 --- /dev/null +++ b/test/634-vdex-duplicate/src/sun/misc/Unsafe.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +package sun.misc; + +public class Unsafe { +} diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 3a475c6fef..0b17656303 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -22,9 +22,52 @@ #include "base/macros.h" #include "openjdkjvmti/jvmti.h" +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + namespace art { namespace Test901HelloTi { +static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) { + jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr); + if (error != JVMTI_ERROR_NONE) { + printf("Failed to enable event"); + } +} + +static void JNICALL VMStartCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED) { + printf("VMStart\n"); +} + +static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED, + jthread thread ATTRIBUTE_UNUSED) { + printf("VMInit\n"); +} + +static void JNICALL VMDeatchCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED, + JNIEnv* jni_env ATTRIBUTE_UNUSED) { + printf("VMDeath\n"); +} + + +static void InstallVMEvents(jvmtiEnv* env) { + jvmtiEventCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); + callbacks.VMStart = VMStartCallback; + callbacks.VMInit = VMInitCallback; + callbacks.VMDeath = VMDeatchCallback; + jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks)); + if (ret != JVMTI_ERROR_NONE) { + printf("Failed to install callbacks"); + } + + EnableEvent(env, JVMTI_EVENT_VM_START); + EnableEvent(env, JVMTI_EVENT_VM_INIT); + EnableEvent(env, JVMTI_EVENT_VM_DEATH); +} + jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { @@ -69,12 +112,52 @@ jint OnLoad(JavaVM* vm, printf("Unexpected version number!\n"); return -1; } + + InstallVMEvents(env); + InstallVMEvents(env2); + CHECK_CALL_SUCCESS(env->DisposeEnvironment()); CHECK_CALL_SUCCESS(env2->DisposeEnvironment()); #undef CHECK_CALL_SUCCESS + + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + SetAllCapabilities(jvmti_env); + + jvmtiPhase current_phase; + jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); + if (phase_result != JVMTI_ERROR_NONE) { + printf("Could not get phase"); + return 1; + } + if (current_phase != JVMTI_PHASE_ONLOAD) { + printf("Wrong phase"); + return 1; + } + + InstallVMEvents(jvmti_env); + return JNI_OK; } +extern "C" JNIEXPORT void JNICALL Java_Main_setVerboseFlag( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jint iflag, jboolean val) { + jvmtiVerboseFlag flag = static_cast<jvmtiVerboseFlag>(iflag); + jvmtiError result = jvmti_env->SetVerboseFlag(flag, val); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jvmtiPhase current_phase; + jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); + if (JvmtiErrorToException(env, phase_result)) { + return JNI_FALSE; + } + return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE; +} } // namespace Test901HelloTi } // namespace art diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt index 414eb3b2ae..c4b24cba90 100644 --- a/test/901-hello-ti-agent/expected.txt +++ b/test/901-hello-ti-agent/expected.txt @@ -1,2 +1,12 @@ Loaded Agent for test 901-hello-ti-agent +VMStart +VMInit Hello, world! +Agent in live phase. +0 +1 +2 +4 +8 +JVMTI_ERROR_ILLEGAL_ARGUMENT +VMDeath diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java index 1ef6289559..faf2dc2591 100644 --- a/test/901-hello-ti-agent/src/Main.java +++ b/test/901-hello-ti-agent/src/Main.java @@ -16,6 +16,31 @@ public class Main { public static void main(String[] args) { + System.loadLibrary(args[1]); + System.out.println("Hello, world!"); + + if (checkLivePhase()) { + System.out.println("Agent in live phase."); + } + + set(0); // OTHER + set(1); // GC + set(2); // CLASS + set(4); // JNI + set(8); // Error. } + + private static void set(int i) { + System.out.println(i); + try { + setVerboseFlag(i, true); + setVerboseFlag(i, false); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + } + + private static native boolean checkLivePhase(); + private static native void setVerboseFlag(int flag, boolean value); } diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index e40698acc5..dad08c9f97 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -4,72 +4,72 @@ From top --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 - print (Ljava/lang/Thread;II)V 0 183 - printOrWait (IILMain$ControlData;)V 6 246 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - doTest ()V 38 41 - main ([Ljava/lang/String;)V 6 27 ---------- - print (Ljava/lang/Thread;II)V 0 183 - printOrWait (IILMain$ControlData;)V 6 246 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - doTest ()V 42 42 - main ([Ljava/lang/String;)V 6 27 + print (Ljava/lang/Thread;II)V 0 34 + printOrWait (IILControlData;)V 6 39 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + doTest ()V 38 23 + main ([Ljava/lang/String;)V 6 21 +--------- + print (Ljava/lang/Thread;II)V 0 34 + printOrWait (IILControlData;)V 6 39 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + doTest ()V 42 24 + main ([Ljava/lang/String;)V 6 21 --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 - print (Ljava/lang/Thread;II)V 0 183 - printOrWait (IILMain$ControlData;)V 6 246 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 ---------- - printOrWait (IILMain$ControlData;)V 6 246 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + print (Ljava/lang/Thread;II)V 0 34 + printOrWait (IILControlData;)V 6 39 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 +--------- + printOrWait (IILControlData;)V 6 39 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 From bottom --------- - main ([Ljava/lang/String;)V 6 27 + main ([Ljava/lang/String;)V 6 21 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - doTest ()V 65 48 - main ([Ljava/lang/String;)V 6 27 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + doTest ()V 65 30 + main ([Ljava/lang/String;)V 6 21 --------- - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 ################################ ### Other thread (suspended) ### @@ -77,135 +77,135 @@ From bottom From top --------- wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 61 ---------- - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 61 ---------- - wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 ---------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 26 +--------- + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 26 +--------- + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 +--------- + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 From bottom --------- - run ()V 4 61 + run ()V 4 26 --------- - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 61 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 26 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 ########################### ### Other thread (live) ### ########################### From top --------- - printOrWait (IILMain$ControlData;)V 44 259 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 95 ---------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 95 ---------- - printOrWait (IILMain$ControlData;)V 44 259 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 ---------- - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 44 52 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 59 +--------- + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 59 +--------- + printOrWait (IILControlData;)V 44 52 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 +--------- + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 From bottom --------- - run ()V 4 95 + run ()V 4 59 --------- - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 95 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 59 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 ################################ ### Other threads (suspended) ### @@ -276,89 +276,89 @@ Signal Catcher --------- Thread-10 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-11 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-12 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-13 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-4 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-5 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-6 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-7 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-8 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- Thread-9 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 --------- main getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 - printAll (I)V 0 219 - doTestAllStackTraces ()V 107 156 - main ([Ljava/lang/String;)V 15 31 + printAll (I)V 0 73 + doTest ()V 102 57 + main ([Ljava/lang/String;)V 30 33 --------- FinalizerDaemon @@ -378,218 +378,459 @@ Signal Catcher --------- Thread-10 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-11 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-12 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-13 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-4 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-5 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-6 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-7 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-8 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- Thread-9 wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 252 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 - bar (IIILMain$ControlData;)J 0 231 - foo (IIILMain$ControlData;)I 0 226 - run ()V 4 144 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 45 --------- main getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 - printAll (I)V 0 219 - doTestAllStackTraces ()V 112 158 - main ([Ljava/lang/String;)V 15 31 + printAll (I)V 0 73 + doTest ()V 107 59 + main ([Ljava/lang/String;)V 30 33 + + +######################################## +### Other select threads (suspended) ### +######################################## +--------- +Thread-14 + +--------- +Thread-16 + +--------- +Thread-18 + +--------- +Thread-20 + +--------- +Thread-22 + +--------- +main + +--------- +Thread-14 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + +--------- +Thread-16 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + +--------- +Thread-18 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + +--------- +Thread-20 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 +--------- +Thread-22 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + +--------- +main + getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 + printList ([Ljava/lang/Thread;I)V 0 66 + doTest ()V 96 52 + main ([Ljava/lang/String;)V 38 37 + +--------- +Thread-14 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 35 + +--------- +Thread-16 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 35 + +--------- +Thread-18 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 35 + +--------- +Thread-20 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 35 + +--------- +Thread-22 + wait ()V -1 -2 + printOrWait (IILControlData;)V 24 45 + baz (IIILControlData;)Ljava/lang/Object; 2 30 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + baz (IIILControlData;)Ljava/lang/Object; 9 32 + bar (IIILControlData;)J 0 24 + foo (IIILControlData;)I 0 19 + run ()V 4 35 + +--------- +main + getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 + printList ([Ljava/lang/Thread;I)V 0 66 + doTest ()V 101 54 + main ([Ljava/lang/String;)V 38 37 + + +################### +### Same thread ### +################### +4 +JVMTI_ERROR_ILLEGAL_ARGUMENT +[public static native java.lang.Object[] Frames.getFrameLocation(java.lang.Thread,int), ffffffff] +[public static void Frames.doTestSameThread(), 38] +[public static void Frames.doTest() throws java.lang.Exception, 0] +[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 2e] +JVMTI_ERROR_NO_MORE_FRAMES + +################################ +### Other thread (suspended) ### +################################ +18 +JVMTI_ERROR_ILLEGAL_ARGUMENT +[public final native void java.lang.Object.wait() throws java.lang.InterruptedException, ffffffff] +[private static void Recurse.printOrWait(int,int,ControlData), 18] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 2] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[public void Frames$1.run(), 4] +JVMTI_ERROR_NO_MORE_FRAMES + +########################### +### Other thread (live) ### +########################### +17 +JVMTI_ERROR_ILLEGAL_ARGUMENT +[private static void Recurse.printOrWait(int,int,ControlData), 2c] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 2] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[private static java.lang.Object Recurse.baz(int,int,int,ControlData), 9] +[private static long Recurse.bar(int,int,int,ControlData), 0] +[public static int Recurse.foo(int,int,int,ControlData), 0] +[public void Frames$2.run(), 4] +JVMTI_ERROR_NO_MORE_FRAMES Done diff --git a/test/911-get-stack-trace/src/AllTraces.java b/test/911-get-stack-trace/src/AllTraces.java new file mode 100644 index 0000000000..adf6f38f5a --- /dev/null +++ b/test/911-get-stack-trace/src/AllTraces.java @@ -0,0 +1,80 @@ +/* + * 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.ArrayList; +import java.util.List; + +public class AllTraces { + private final static List<Object> RETAIN = new ArrayList<Object>(); + + public static void doTest() throws Exception { + System.out.println("################################"); + System.out.println("### Other threads (suspended) ###"); + System.out.println("################################"); + + // Also create an unstarted and a dead thread. + RETAIN.add(new Thread()); + Thread deadThread = new Thread(); + RETAIN.add(deadThread); + deadThread.start(); + deadThread.join(); + + final int N = 10; + + final ControlData data = new ControlData(N); + data.waitFor = new Object(); + + Thread threads[] = new Thread[N]; + + for (int i = 0; i < N; i++) { + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + threads[i] = t; + } + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + printAll(0); + + printAll(5); + + printAll(25); + + // Let the thread make progress and die. + synchronized(data.waitFor) { + data.waitFor.notifyAll(); + } + for (int i = 0; i < N; i++) { + threads[i].join(); + } + + RETAIN.clear(); + } + + public static void printAll(int max) { + PrintThread.printAll(getAllStackTraces(max)); + } + + // Get all stack traces. This will return an array with an element for each thread. The element + // is an array itself with the first element being the thread, and the second element a nested + // String array as in getStackTrace. + public static native Object[][] getAllStackTraces(int max); +} diff --git a/test/911-get-stack-trace/src/ControlData.java b/test/911-get-stack-trace/src/ControlData.java new file mode 100644 index 0000000000..76ac4b8caa --- /dev/null +++ b/test/911-get-stack-trace/src/ControlData.java @@ -0,0 +1,31 @@ +/* + * 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.concurrent.CountDownLatch; + +public class ControlData { + CountDownLatch reached; + Object waitFor = null; + volatile boolean stop = false; + + public ControlData() { + this(1); + } + + public ControlData(int latchCount) { + reached = new CountDownLatch(latchCount); + } +} diff --git a/test/911-get-stack-trace/src/Frames.java b/test/911-get-stack-trace/src/Frames.java new file mode 100644 index 0000000000..a1a11c3785 --- /dev/null +++ b/test/911-get-stack-trace/src/Frames.java @@ -0,0 +1,133 @@ +/* + * 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.util.Arrays; + +public class Frames { + public static void doTest() throws Exception { + doTestSameThread(); + + System.out.println(); + + doTestOtherThreadWait(); + + System.out.println(); + + doTestOtherThreadBusyLoop(); + } + + public static void doTestSameThread() { + System.out.println("###################"); + System.out.println("### Same thread ###"); + System.out.println("###################"); + + Thread t = Thread.currentThread(); + + int count = getFrameCount(t); + System.out.println(count); + try { + System.out.println(Arrays.toString(getFrameLocation(t, -1))); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + for (int i = 0; i < count; i++) { + System.out.println(Arrays.toString(getFrameLocation(t, i))); + } + try { + System.out.println(Arrays.toString(getFrameLocation(t, count))); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + } + + public static void doTestOtherThreadWait() throws Exception { + System.out.println("################################"); + System.out.println("### Other thread (suspended) ###"); + System.out.println("################################"); + final ControlData data = new ControlData(); + data.waitFor = new Object(); + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + int count = getFrameCount(t); + System.out.println(count); + try { + System.out.println(Arrays.toString(getFrameLocation(t, -1))); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + for (int i = 0; i < count; i++) { + System.out.println(Arrays.toString(getFrameLocation(t, i))); + } + try { + System.out.println(Arrays.toString(getFrameLocation(t, count))); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + + // Let the thread make progress and die. + synchronized(data.waitFor) { + data.waitFor.notifyAll(); + } + t.join(); + } + + public static void doTestOtherThreadBusyLoop() throws Exception { + System.out.println("###########################"); + System.out.println("### Other thread (live) ###"); + System.out.println("###########################"); + final ControlData data = new ControlData(); + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + int count = getFrameCount(t); + System.out.println(count); + try { + System.out.println(Arrays.toString(getFrameLocation(t, -1))); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + for (int i = 0; i < count; i++) { + System.out.println(Arrays.toString(getFrameLocation(t, i))); + } + try { + System.out.println(Arrays.toString(getFrameLocation(t, count))); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + + // Let the thread stop looping and die. + data.stop = true; + t.join(); + } + + public static native int getFrameCount(Thread thread); + public static native Object[] getFrameLocation(Thread thread, int depth); +} diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java index 3479abbeae..b199033c32 100644 --- a/test/911-get-stack-trace/src/Main.java +++ b/test/911-get-stack-trace/src/Main.java @@ -14,273 +14,32 @@ * limitations under the License. */ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; - public class Main { public static void main(String[] args) throws Exception { System.loadLibrary(args[1]); - doTest(); - doTestOtherThreadWait(); - doTestOtherThreadBusyLoop(); - - doTestAllStackTraces(); - - System.out.println("Done"); - } - - public static void doTest() throws Exception { - System.out.println("###################"); - System.out.println("### Same thread ###"); - System.out.println("###################"); - System.out.println("From top"); - Recurse.foo(4, 0, 25, null); - Recurse.foo(4, 1, 25, null); - Recurse.foo(4, 0, 5, null); - Recurse.foo(4, 2, 5, null); + SameThread.doTest(); - System.out.println("From bottom"); - Recurse.foo(4, -1, 25, null); - Recurse.foo(4, -5, 5, null); - Recurse.foo(4, -7, 5, null); - } - - public static void doTestOtherThreadWait() throws Exception { System.out.println(); - System.out.println("################################"); - System.out.println("### Other thread (suspended) ###"); - System.out.println("################################"); - final ControlData data = new ControlData(); - data.waitFor = new Object(); - Thread t = new Thread() { - public void run() { - Recurse.foo(4, 0, 0, data); - } - }; - t.start(); - data.reached.await(); - Thread.yield(); - Thread.sleep(500); // A little bit of time... - - System.out.println("From top"); - print(t, 0, 25); - print(t, 1, 25); - print(t, 0, 5); - print(t, 2, 5); - System.out.println("From bottom"); - print(t, -1, 25); - print(t, -5, 5); - print(t, -7, 5); - - // Let the thread make progress and die. - synchronized(data.waitFor) { - data.waitFor.notifyAll(); - } - t.join(); - } + OtherThread.doTestOtherThreadWait(); - public static void doTestOtherThreadBusyLoop() throws Exception { System.out.println(); - System.out.println("###########################"); - System.out.println("### Other thread (live) ###"); - System.out.println("###########################"); - final ControlData data = new ControlData(); - Thread t = new Thread() { - public void run() { - Recurse.foo(4, 0, 0, data); - } - }; - t.start(); - data.reached.await(); - Thread.yield(); - Thread.sleep(500); // A little bit of time... - System.out.println("From top"); - print(t, 0, 25); - print(t, 1, 25); - print(t, 0, 5); - print(t, 2, 5); + OtherThread.doTestOtherThreadBusyLoop(); - System.out.println("From bottom"); - print(t, -1, 25); - print(t, -5, 5); - print(t, -7, 5); - - // Let the thread stop looping and die. - data.stop = true; - t.join(); - } - - private final static List<Object> RETAIN = new ArrayList<Object>(); - - public static void doTestAllStackTraces() throws Exception { System.out.println(); - System.out.println("################################"); - System.out.println("### Other threads (suspended) ###"); - System.out.println("################################"); - - // Also create an unstarted and a dead thread. - RETAIN.add(new Thread()); - Thread deadThread = new Thread(); - RETAIN.add(deadThread); - deadThread.start(); - deadThread.join(); - - final int N = 10; - - final ControlData data = new ControlData(N); - data.waitFor = new Object(); - - Thread threads[] = new Thread[N]; - - for (int i = 0; i < N; i++) { - Thread t = new Thread() { - public void run() { - Recurse.foo(4, 0, 0, data); - } - }; - t.start(); - threads[i] = t; - } - data.reached.await(); - Thread.yield(); - Thread.sleep(500); // A little bit of time... - - printAll(0); - - printAll(5); - printAll(25); + AllTraces.doTest(); - // Let the thread make progress and die. - synchronized(data.waitFor) { - data.waitFor.notifyAll(); - } - for (int i = 0; i < N; i++) { - threads[i].join(); - } - - RETAIN.clear(); - } - - public static void print(String[][] stack) { - System.out.println("---------"); - for (String[] stackElement : stack) { - for (String part : stackElement) { - System.out.print(' '); - System.out.print(part); - } - System.out.println(); - } - } - - public static void print(Thread t, int start, int max) { - print(getStackTrace(t, start, max)); - } - - public static void printAll(Object[][] stacks) { - List<String> stringified = new ArrayList<String>(stacks.length); - - for (Object[] stackInfo : stacks) { - Thread t = (Thread)stackInfo[0]; - String name = (t != null) ? t.getName() : "null"; - String stackSerialization; - if (name.contains("Daemon")) { - // Do not print daemon stacks, as they're non-deterministic. - stackSerialization = "<not printed>"; - } else { - StringBuilder sb = new StringBuilder(); - for (String[] stackElement : (String[][])stackInfo[1]) { - for (String part : stackElement) { - sb.append(' '); - sb.append(part); - } - sb.append('\n'); - } - stackSerialization = sb.toString(); - } - stringified.add(name + "\n" + stackSerialization); - } - - Collections.sort(stringified); - - for (String s : stringified) { - System.out.println("---------"); - System.out.println(s); - } - } - - public static void printAll(int max) { - printAll(getAllStackTraces(max)); - } - - // Wrap generated stack traces into a class to separate them nicely. - public static class Recurse { - - public static int foo(int x, int start, int max, ControlData data) { - bar(x, start, max, data); - return 0; - } - - private static long bar(int x, int start, int max, ControlData data) { - baz(x, start, max, data); - return 0; - } - - private static Object baz(int x, int start, int max, ControlData data) { - if (x == 0) { - printOrWait(start, max, data); - } else { - foo(x - 1, start, max, data); - } - return null; - } + System.out.println(); - private static void printOrWait(int start, int max, ControlData data) { - if (data == null) { - print(Thread.currentThread(), start, max); - } else { - if (data.waitFor != null) { - synchronized (data.waitFor) { - data.reached.countDown(); - try { - data.waitFor.wait(); // Use wait() as it doesn't have a "hidden" Java call-graph. - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - } else { - data.reached.countDown(); - while (!data.stop) { - // Busy-loop. - } - } - } - } - } + ThreadListTraces.doTest(); - public static class ControlData { - CountDownLatch reached; - Object waitFor = null; - volatile boolean stop = false; + System.out.println(); - public ControlData() { - this(1); - } + Frames.doTest(); - public ControlData(int latchCount) { - reached = new CountDownLatch(latchCount); - } + System.out.println("Done"); } - - public static native String[][] getStackTrace(Thread thread, int start, int max); - // Get all stack traces. This will return an array with an element for each thread. The element - // is an array itself with the first element being the thread, and the second element a nested - // String array as in getStackTrace. - public static native Object[][] getAllStackTraces(int max); } diff --git a/test/911-get-stack-trace/src/OtherThread.java b/test/911-get-stack-trace/src/OtherThread.java new file mode 100644 index 0000000000..0748433a20 --- /dev/null +++ b/test/911-get-stack-trace/src/OtherThread.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +public class OtherThread { + public static void doTestOtherThreadWait() throws Exception { + System.out.println("################################"); + System.out.println("### Other thread (suspended) ###"); + System.out.println("################################"); + final ControlData data = new ControlData(); + data.waitFor = new Object(); + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + System.out.println("From top"); + PrintThread.print(t, 0, 25); + PrintThread.print(t, 1, 25); + PrintThread.print(t, 0, 5); + PrintThread.print(t, 2, 5); + + System.out.println("From bottom"); + PrintThread.print(t, -1, 25); + PrintThread.print(t, -5, 5); + PrintThread.print(t, -7, 5); + + // Let the thread make progress and die. + synchronized(data.waitFor) { + data.waitFor.notifyAll(); + } + t.join(); + } + + public static void doTestOtherThreadBusyLoop() throws Exception { + System.out.println("###########################"); + System.out.println("### Other thread (live) ###"); + System.out.println("###########################"); + final ControlData data = new ControlData(); + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + System.out.println("From top"); + PrintThread.print(t, 0, 25); + PrintThread.print(t, 1, 25); + PrintThread.print(t, 0, 5); + PrintThread.print(t, 2, 5); + + System.out.println("From bottom"); + PrintThread.print(t, -1, 25); + PrintThread.print(t, -5, 5); + PrintThread.print(t, -7, 5); + + // Let the thread stop looping and die. + data.stop = true; + t.join(); + } +} diff --git a/test/911-get-stack-trace/src/PrintThread.java b/test/911-get-stack-trace/src/PrintThread.java new file mode 100644 index 0000000000..97815ccad9 --- /dev/null +++ b/test/911-get-stack-trace/src/PrintThread.java @@ -0,0 +1,70 @@ +/* + * 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.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PrintThread { + public static void print(String[][] stack) { + System.out.println("---------"); + for (String[] stackElement : stack) { + for (String part : stackElement) { + System.out.print(' '); + System.out.print(part); + } + System.out.println(); + } + } + + public static void print(Thread t, int start, int max) { + print(getStackTrace(t, start, max)); + } + + public static void printAll(Object[][] stacks) { + List<String> stringified = new ArrayList<String>(stacks.length); + + for (Object[] stackInfo : stacks) { + Thread t = (Thread)stackInfo[0]; + String name = (t != null) ? t.getName() : "null"; + String stackSerialization; + if (name.contains("Daemon")) { + // Do not print daemon stacks, as they're non-deterministic. + stackSerialization = "<not printed>"; + } else { + StringBuilder sb = new StringBuilder(); + for (String[] stackElement : (String[][])stackInfo[1]) { + for (String part : stackElement) { + sb.append(' '); + sb.append(part); + } + sb.append('\n'); + } + stackSerialization = sb.toString(); + } + stringified.add(name + "\n" + stackSerialization); + } + + Collections.sort(stringified); + + for (String s : stringified) { + System.out.println("---------"); + System.out.println(s); + } + } + + public static native String[][] getStackTrace(Thread thread, int start, int max); +}
\ No newline at end of file diff --git a/test/911-get-stack-trace/src/Recurse.java b/test/911-get-stack-trace/src/Recurse.java new file mode 100644 index 0000000000..439fbaaf84 --- /dev/null +++ b/test/911-get-stack-trace/src/Recurse.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +public class Recurse { + public static int foo(int x, int start, int max, ControlData data) { + bar(x, start, max, data); + return 0; + } + + private static long bar(int x, int start, int max, ControlData data) { + baz(x, start, max, data); + return 0; + } + + private static Object baz(int x, int start, int max, ControlData data) { + if (x == 0) { + printOrWait(start, max, data); + } else { + foo(x - 1, start, max, data); + } + return null; + } + + private static void printOrWait(int start, int max, ControlData data) { + if (data == null) { + PrintThread.print(Thread.currentThread(), start, max); + } else { + if (data.waitFor != null) { + synchronized (data.waitFor) { + data.reached.countDown(); + try { + data.waitFor.wait(); // Use wait() as it doesn't have a "hidden" Java call-graph. + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + } else { + data.reached.countDown(); + while (!data.stop) { + // Busy-loop. + } + } + } + } +}
\ No newline at end of file diff --git a/test/911-get-stack-trace/src/SameThread.java b/test/911-get-stack-trace/src/SameThread.java new file mode 100644 index 0000000000..f1e19e367a --- /dev/null +++ b/test/911-get-stack-trace/src/SameThread.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +public class SameThread { + public static void doTest() throws Exception { + System.out.println("###################"); + System.out.println("### Same thread ###"); + System.out.println("###################"); + System.out.println("From top"); + Recurse.foo(4, 0, 25, null); + Recurse.foo(4, 1, 25, null); + Recurse.foo(4, 0, 5, null); + Recurse.foo(4, 2, 5, null); + + System.out.println("From bottom"); + Recurse.foo(4, -1, 25, null); + Recurse.foo(4, -5, 5, null); + Recurse.foo(4, -7, 5, null); + } +} diff --git a/test/911-get-stack-trace/src/ThreadListTraces.java b/test/911-get-stack-trace/src/ThreadListTraces.java new file mode 100644 index 0000000000..f66557f3bd --- /dev/null +++ b/test/911-get-stack-trace/src/ThreadListTraces.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +public class ThreadListTraces { + public static void doTest() throws Exception { + System.out.println("########################################"); + System.out.println("### Other select threads (suspended) ###"); + System.out.println("########################################"); + + final int N = 10; + + final ControlData data = new ControlData(N); + data.waitFor = new Object(); + + Thread threads[] = new Thread[N]; + + Thread list[] = new Thread[N/2 + 1]; + + for (int i = 0; i < N; i++) { + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + threads[i] = t; + if (i % 2 == 0) { + list[i/2] = t; + } + } + list[list.length - 1] = Thread.currentThread(); + + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + printList(list, 0); + + printList(list, 5); + + printList(list, 25); + + // Let the thread make progress and die. + synchronized(data.waitFor) { + data.waitFor.notifyAll(); + } + for (int i = 0; i < N; i++) { + threads[i].join(); + } + } + + public static void printList(Thread[] threads, int max) { + PrintThread.printAll(getThreadListStackTraces(threads, max)); + } + + // Similar to getAllStackTraces, but restricted to the given threads. + public static native Object[][] getThreadListStackTraces(Thread threads[], int max); +} diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index 57d4f6d2ee..d162e8a169 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -20,6 +20,7 @@ #include "android-base/stringprintf.h" +#include "android-base/stringprintf.h" #include "base/logging.h" #include "base/macros.h" #include "jni.h" @@ -59,11 +60,7 @@ static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env, char* gen; { jvmtiError result2 = jvmti_env->GetMethodName(frames[method_index].method, &name, &sig, &gen); - if (result2 != JVMTI_ERROR_NONE) { - char* err; - jvmti_env->GetErrorName(result2, &err); - printf("Failure running GetMethodName: %s\n", err); - jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); + if (JvmtiErrorToException(env, result2)) { return nullptr; } } @@ -127,18 +124,14 @@ static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env, return CreateObjectArray(env, count, "[Ljava/lang/String;", callback); } -extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( +extern "C" JNIEXPORT jobjectArray JNICALL Java_PrintThread_getStackTrace( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) { std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); jint count; { jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count); - if (result != JVMTI_ERROR_NONE) { - char* err; - jvmti_env->GetErrorName(result, &err); - printf("Failure running GetStackTrace: %s\n", err); - jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); + if (JvmtiErrorToException(env, result)) { return nullptr; } } @@ -146,19 +139,13 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( return TranslateJvmtiFrameInfoArray(env, frames.get(), count); } -extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllStackTraces( +extern "C" JNIEXPORT jobjectArray JNICALL Java_AllTraces_getAllStackTraces( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint max) { - std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); - jint thread_count; jvmtiStackInfo* stack_infos; { jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count); - if (result != JVMTI_ERROR_NONE) { - char* err; - jvmti_env->GetErrorName(result, &err); - printf("Failure running GetAllStackTraces: %s\n", err); - jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); + if (JvmtiErrorToException(env, result)) { return nullptr; } } @@ -180,5 +167,91 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllStackTraces( return ret; } +extern "C" JNIEXPORT jobjectArray JNICALL Java_ThreadListTraces_getThreadListStackTraces( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobjectArray jthreads, jint max) { + jint thread_count = env->GetArrayLength(jthreads); + std::unique_ptr<jthread[]> threads(new jthread[thread_count]); + for (jint i = 0; i != thread_count; ++i) { + threads[i] = env->GetObjectArrayElement(jthreads, i); + } + + jvmtiStackInfo* stack_infos; + { + jvmtiError result = jvmti_env->GetThreadListStackTraces(thread_count, + threads.get(), + max, + &stack_infos); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + } + + auto callback = [&](jint thread_index) -> jobject { + auto inner_callback = [&](jint index) -> jobject { + if (index == 0) { + return stack_infos[thread_index].thread; + } else { + return TranslateJvmtiFrameInfoArray(env, + stack_infos[thread_index].frame_buffer, + stack_infos[thread_index].frame_count); + } + }; + return CreateObjectArray(env, 2, "java/lang/Object", inner_callback); + }; + jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos)); + return ret; +} + +extern "C" JNIEXPORT jint JNICALL Java_Frames_getFrameCount( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread) { + jint count; + jvmtiError result = jvmti_env->GetFrameCount(thread, &count); + if (JvmtiErrorToException(env, result)) { + return -1; + } + return count; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Frames_getFrameLocation( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint depth) { + jmethodID method; + jlocation location; + + jvmtiError result = jvmti_env->GetFrameLocation(thread, depth, &method, &location); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + switch (index) { + case 0: + { + jclass decl_class; + jvmtiError class_result = jvmti_env->GetMethodDeclaringClass(method, &decl_class); + if (JvmtiErrorToException(env, class_result)) { + return nullptr; + } + jint modifiers; + jvmtiError mod_result = jvmti_env->GetMethodModifiers(method, &modifiers); + if (JvmtiErrorToException(env, mod_result)) { + return nullptr; + } + constexpr jint kStatic = 0x8; + return env->ToReflectedMethod(decl_class, + method, + (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE); + } + case 1: + return env->NewStringUTF( + android::base::StringPrintf("%x", static_cast<uint32_t>(location)).c_str()); + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + }; + jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback); + return ret; +} + } // namespace Test911GetStackTrace } // namespace art diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index 69301c7925..29eeff6694 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -222,5 +222,42 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader( return classloader; } +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) { + jint count = 0; + jclass* classes = nullptr; + jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint i) { + return classes[i]; + }; + jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback); + if (classes != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes)); + } + return ret; +} + +extern "C" JNIEXPORT jintArray JNICALL Java_Main_getClassVersion( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + jint major, minor; + jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + jintArray int_array = env->NewIntArray(2); + if (int_array == nullptr) { + return nullptr; + } + jint buf[2] = { major, minor }; + env->SetIntArrayRegion(int_array, 0, 2, buf); + + return int_array; +} + } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index 44c861a3b7..f3cb261480 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -43,3 +43,21 @@ class java.lang.String null class [Ljava.lang.String; null interface Main$InfA dalvik.system.PathClassLoader class $Proxy0 dalvik.system.PathClassLoader + +boot <- src <- src-ex (A,B) +912-classes-ex.jar+ -> 912-classes.jar+ -> +[class A, class B, class java.lang.Object] +912-classes.jar+ -> +[class B, class java.lang.Object] + +boot <- src (B) <- src-ex (A, List) +912-classes-ex.jar+ -> 912-classes.jar+ -> +[class A, class java.lang.Object, interface java.util.List] +912-classes.jar+ -> +[class B, class java.lang.Object] + +boot <- src+src-ex (A,B) +912-classes.jar+ -> +[class A, class B, class java.lang.Object] + +[37, 0] diff --git a/test/912-classes/run b/test/912-classes/run index 4379349cb2..20dfc4b767 100755 --- a/test/912-classes/run +++ b/test/912-classes/run @@ -14,6 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +# This test checks which classes are initiated by a classloader. App images preload classes. +# 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 + --jvmti \ + --no-app-image diff --git a/test/912-classes/src-ex/A.java b/test/912-classes/src-ex/A.java new file mode 100644 index 0000000000..64acb2fcfe --- /dev/null +++ b/test/912-classes/src-ex/A.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class A { +}
\ No newline at end of file diff --git a/test/912-classes/src/B.java b/test/912-classes/src/B.java new file mode 100644 index 0000000000..f1458c3bca --- /dev/null +++ b/test/912-classes/src/B.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class B { +}
\ No newline at end of file diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index e627d4227a..cbf2392a73 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -14,8 +14,10 @@ * limitations under the License. */ +import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Comparator; public class Main { public static void main(String[] args) throws Exception { @@ -76,6 +78,12 @@ public class Main { testClassLoader(String[].class); testClassLoader(InfA.class); testClassLoader(getProxyClass()); + + testClassLoaderClasses(); + + System.out.println(); + + testClassVersion(); } private static Class<?> proxyClass = null; @@ -151,6 +159,99 @@ public class Main { } } + private static void testClassLoaderClasses() throws Exception { + ClassLoader boot = ClassLoader.getSystemClassLoader().getParent(); + while (boot.getParent() != null) { + boot = boot.getParent(); + } + + System.out.println(); + System.out.println("boot <- src <- src-ex (A,B)"); + ClassLoader cl1 = create(create(boot, DEX1), DEX2); + Class.forName("B", false, cl1); + Class.forName("A", false, cl1); + printClassLoaderClasses(cl1); + + System.out.println(); + System.out.println("boot <- src (B) <- src-ex (A, List)"); + ClassLoader cl2 = create(create(boot, DEX1), DEX2); + Class.forName("A", false, cl2); + Class.forName("java.util.List", false, cl2); + Class.forName("B", false, cl2.getParent()); + printClassLoaderClasses(cl2); + + System.out.println(); + System.out.println("boot <- src+src-ex (A,B)"); + ClassLoader cl3 = create(boot, DEX1, DEX2); + Class.forName("B", false, cl3); + Class.forName("A", false, cl3); + printClassLoaderClasses(cl3); + + // Check that the boot classloader dumps something non-empty. + Class<?>[] bootClasses = getClassLoaderClasses(boot); + if (bootClasses.length == 0) { + throw new RuntimeException("No classes initiated by boot classloader."); + } + // Check that at least java.util.List is loaded. + boolean foundList = false; + for (Class<?> c : bootClasses) { + if (c == java.util.List.class) { + foundList = true; + break; + } + } + if (!foundList) { + System.out.println(Arrays.toString(bootClasses)); + throw new RuntimeException("Could not find class java.util.List."); + } + } + + private static void testClassVersion() { + System.out.println(Arrays.toString(getClassVersion(Main.class))); + } + + private static void printClassLoaderClasses(ClassLoader cl) { + for (;;) { + if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { + break; + } + + ClassLoader saved = cl; + for (;;) { + if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { + break; + } + String s = cl.toString(); + int index1 = s.indexOf("zip file"); + int index2 = s.indexOf(']', index1); + if (index2 < 0) { + throw new RuntimeException("Unexpected classloader " + s); + } + String zip_file = s.substring(index1, index2); + int index3 = zip_file.indexOf('"'); + int index4 = zip_file.indexOf('"', index3 + 1); + if (index4 < 0) { + throw new RuntimeException("Unexpected classloader " + s); + } + String paths = zip_file.substring(index3 + 1, index4); + String pathArray[] = paths.split(":"); + for (String path : pathArray) { + int index5 = path.lastIndexOf('/'); + System.out.print(path.substring(index5 + 1)); + System.out.print('+'); + } + System.out.print(" -> "); + cl = cl.getParent(); + } + System.out.println(); + Class<?> classes[] = getClassLoaderClasses(saved); + Arrays.sort(classes, new ClassNameComparator()); + System.out.println(Arrays.toString(classes)); + + cl = saved.getParent(); + } + } + private static native boolean isModifiableClass(Class<?> c); private static native String[] getClassSignature(Class<?> c); @@ -161,12 +262,16 @@ public class Main { private static native Object[] getClassFields(Class<?> c); private static native Object[] getClassMethods(Class<?> c); - private static native Class[] getImplementedInterfaces(Class<?> c); + private static native Class<?>[] getImplementedInterfaces(Class<?> c); private static native int getClassStatus(Class<?> c); private static native Object getClassLoader(Class<?> c); + private static native Class<?>[] getClassLoaderClasses(ClassLoader cl); + + private static native int[] getClassVersion(Class<?> c); + private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. } @@ -188,4 +293,23 @@ public class Main { } public abstract static class ClassC implements InfA, InfC { } + + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar"; + private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar"; + + private static ClassLoader create(ClassLoader parent, String... elements) throws Exception { + // Note: We use a PathClassLoader, as we do not care about code performance. We only load + // the classes, and they're empty. + Class<?> pathClassLoaderClass = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> pathClassLoaderInit = pathClassLoaderClass.getConstructor(String.class, + ClassLoader.class); + String path = String.join(":", elements); + return (ClassLoader) pathClassLoaderInit.newInstance(path, parent); + } + + private static class ClassNameComparator implements Comparator<Class<?>> { + public int compare(Class<?> c1, Class<?> c2) { + return c1.getName().compareTo(c2.getName()); + } + } } diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java index 74eb003d5c..1e43f7ee9d 100644 --- a/test/916-obsolete-jit/src/Main.java +++ b/test/916-obsolete-jit/src/Main.java @@ -157,38 +157,13 @@ public class Main { doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); } }; - // This does nothing. - Runnable noop = () -> {}; // This just prints something out to show we are running the Runnable. Runnable say_nothing = () -> { w.accept("Not doing anything here"); }; - // This checks to see if we have jitted the methods we are testing. - Runnable check_interpreting = () -> { - // TODO remove the second check when we remove the doCall function. We need to check that - // both of these functions aren't being interpreted because if sayHi is the test doesn't do - // anything and if doCall is then there will be a runtime call right above the sayHi - // function preventing sayHi from being deoptimized. - interpreting = has_jit && (Main.isInterpretedFunction(say_hi_method, true) || - Main.isInterpretedFunction(do_call_method, false)); - }; do { - w.clear(); - // Wait for the methods to be jitted - long j = 0; - do { - for (int i = 0; i < 10000; i++) { - t.sayHi(noop, w); - j++; - // Clear so that we won't OOM if we go around a few times. - w.clear(); - } - t.sayHi(check_interpreting, w); - if (j >= 1000000) { - System.out.println("FAIL: Could not make sayHi be Jitted!"); - return; - } - j++; - } while(interpreting); - // Clear output. Now we try for real. + // Run ensureJitCompiled here since it might get GCd + ensureJitCompiled(Transform.class, "sayHi"); + ensureJitCompiled(Main.class, "doCall"); + // Clear output. w.clear(); // Try and redefine. t.sayHi(say_nothing, w); @@ -203,6 +178,8 @@ public class Main { private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable); + private static native void ensureJitCompiled(Class c, String name); + // Transforms the class private static native void doCommonClassRedefinition(Class<?> target, byte[] classfile, diff --git a/test/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt index e2665ef30b..9615e6b33d 100644 --- a/test/921-hello-failure/expected.txt +++ b/test/921-hello-failure/expected.txt @@ -13,3 +13,19 @@ hello2 - MissingInterface hello2 - ReorderInterface Transformation error : java.lang.Exception(Failed to redefine class <LTransform2;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED) hello2 - ReorderInterface +hello - MultiRedef +hello2 - MultiRedef +Transformation error : java.lang.Exception(Failed to redefine classes <LTransform2;, LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH) +hello - MultiRedef +hello2 - MultiRedef +Transformation error : java.lang.Exception(Failed to redefine classes <LTransform;, LTransform2;> due to JVMTI_ERROR_NAMES_DONT_MATCH) +hello - MultiRedef +hello2 - MultiRedef +hello - MultiRetrans +hello2 - MultiRetrans +Transformation error : java.lang.Exception(Failed to retransform classes <LTransform2;, LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH) +hello - MultiRetrans +hello2 - MultiRetrans +Transformation error : java.lang.Exception(Failed to retransform classes <LTransform;, LTransform2;> due to JVMTI_ERROR_NAMES_DONT_MATCH) +hello - MultiRetrans +hello2 - MultiRetrans diff --git a/test/921-hello-failure/src/CommonClassDefinition.java b/test/921-hello-failure/src/CommonClassDefinition.java new file mode 100644 index 0000000000..62602a02e9 --- /dev/null +++ b/test/921-hello-failure/src/CommonClassDefinition.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } +} diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java index 69c48e26cc..43d6e9ed07 100644 --- a/test/921-hello-failure/src/Main.java +++ b/test/921-hello-failure/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.util.ArrayList; public class Main { public static void main(String[] args) { @@ -23,10 +24,44 @@ public class Main { NewInterface.doTest(new Transform2()); MissingInterface.doTest(new Transform2()); ReorderInterface.doTest(new Transform2()); + MultiRedef.doTest(new Transform(), new Transform2()); + MultiRetrans.doTest(new Transform(), new Transform2()); } // Transforms the class. This throws an exception if something goes wrong. public static native void doCommonClassRedefinition(Class<?> target, byte[] classfile, byte[] dexfile) throws Exception; + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) throws Exception { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) throws Exception { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles) throws Exception; + public static native void doCommonClassRetransformation(Class<?>... target) throws Exception; + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); } diff --git a/test/921-hello-failure/src/MultiRedef.java b/test/921-hello-failure/src/MultiRedef.java new file mode 100644 index 0000000000..c64342c8f8 --- /dev/null +++ b/test/921-hello-failure/src/MultiRedef.java @@ -0,0 +1,100 @@ +/* + * 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.util.Base64; + +class MultiRedef { + + // class NotTransform { + // public void sayHi(String name) { + // throw new Error("Should not be called!"); + // } + // } + private static CommonClassDefinition INVALID_DEFINITION_T1 = new CommonClassDefinition( + Transform.class, + Base64.getDecoder().decode( + "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" + + "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" + + "aWxlAQARTm90VHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBu" + + "b3QgYmUgY2FsbGVkIQwABwAMAQAMTm90VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAgAAUA" + + "BgAAAAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwA" + + "AQAJAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO"), + Base64.getDecoder().decode( + "ZGV4CjAzNQDLV95i5xnv6iUi6uIeDoY5jP5Xe9NP1AiYAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAL" + + "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACQAQAACAEAAEoB" + + "AABSAQAAYgEAAHUBAACJAQAAnQEAALABAADHAQAAygEAAM4BAADiAQAAAQAAAAIAAAADAAAABAAA" + + "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" + + "AAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAPQBAAAAAAAAAQABAAEAAADpAQAABAAAAHAQAwAA" + + "AA4ABAACAAIAAADuAQAACQAAACIAAQAbAQYAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgAOTE5v" + + "dFRyYW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" + + "L2xhbmcvU3RyaW5nOwARTm90VHJhbnNmb3JtLmphdmEAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAB" + + "VgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAICABIgCAQGg" + + "AgAADAAAAAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAFAAAAnAAAAAMAAAACAAAAsAAAAAUA" + + "AAAEAAAAyAAAAAYAAAABAAAA6AAAAAEgAAACAAAACAEAAAEQAAABAAAARAEAAAIgAAALAAAASgEA" + + "AAMgAAACAAAA6QEAAAAgAAABAAAA9AEAAAAQAAABAAAABAIAAA==")); + + // Valid redefinition of Transform2 + // class Transform2 implements Iface1, Iface2 { + // public void sayHi(String name) { + // throw new Error("Should not be called!"); + // } + // } + private static CommonClassDefinition VALID_DEFINITION_T2 = new CommonClassDefinition( + Transform2.class, + Base64.getDecoder().decode( + "yv66vgAAADQAGQoABgARBwASCAATCgACABQHABUHABYHABcHABgBAAY8aW5pdD4BAAMoKVYBAARD" + + "b2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApT" + + "b3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91" + + "bGQgbm90IGJlIGNhbGxlZCEMAAkADgEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAG" + + "SWZhY2UxAQAGSWZhY2UyACAABQAGAAIABwAIAAAAAgAAAAkACgABAAsAAAAdAAEAAQAAAAUqtwAB" + + "sQAAAAEADAAAAAYAAQAAAAEAAQANAA4AAQALAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEADAAA" + + "AAYAAQAAAAMAAQAPAAAAAgAQ"), + Base64.getDecoder().decode( + "ZGV4CjAzNQDSWls05CPkX+gbTGMVRvx9dc9vozzVbu7AAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAN" + + "AAAAcAAAAAcAAACkAAAAAgAAAMAAAAAAAAAAAAAAAAQAAADYAAAAAQAAAPgAAACoAQAAGAEAAGIB" + + "AABqAQAAdAEAAH4BAACMAQAAnwEAALMBAADHAQAA3gEAAO8BAADyAQAA9gEAAAoCAAABAAAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAABcAQAAAgAAAAAAAAACAAEA" + + "DAAAAAMAAQAAAAAABAAAAAAAAAACAAAAAAAAAAQAAABUAQAACAAAAAAAAAAcAgAAAAAAAAEAAQAB" + + "AAAAEQIAAAQAAABwEAMAAAAOAAQAAgACAAAAFgIAAAkAAAAiAAMAGwEHAAAAcCACABAAJwAAAAIA" + + "AAAAAAEAAQAAAAUABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAxMVHJhbnNmb3JtMjsAEUxq" + + "YXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAV" + + "U2hvdWxkIG5vdCBiZSBjYWxsZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBq" + + "YWNrLTQuMjAABXNheUhpAAEABw4AAwEABw4AAAABAQCAgASYAgEBsAIAAAwAAAAAAAAAAQAAAAAA" + + "AAABAAAADQAAAHAAAAACAAAABwAAAKQAAAADAAAAAgAAAMAAAAAFAAAABAAAANgAAAAGAAAAAQAA" + + "APgAAAABIAAAAgAAABgBAAABEAAAAgAAAFQBAAACIAAADQAAAGIBAAADIAAAAgAAABECAAAAIAAA" + + "AQAAABwCAAAAEAAAAQAAACwCAAA=")); + + public static void doTest(Transform t1, Transform2 t2) { + t1.sayHi("MultiRedef"); + t2.sayHi("MultiRedef"); + try { + Main.doMultiClassRedefinition(VALID_DEFINITION_T2, INVALID_DEFINITION_T1); + } catch (Exception e) { + System.out.println( + "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")"); + } + t1.sayHi("MultiRedef"); + t2.sayHi("MultiRedef"); + try { + Main.doMultiClassRedefinition(INVALID_DEFINITION_T1, VALID_DEFINITION_T2); + } catch (Exception e) { + System.out.println( + "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")"); + } + t1.sayHi("MultiRedef"); + t2.sayHi("MultiRedef"); + } +} diff --git a/test/921-hello-failure/src/MultiRetrans.java b/test/921-hello-failure/src/MultiRetrans.java new file mode 100644 index 0000000000..95aaf074e9 --- /dev/null +++ b/test/921-hello-failure/src/MultiRetrans.java @@ -0,0 +1,108 @@ +/* + * 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.util.Base64; + +class MultiRetrans { + + // class NotTransform { + // public void sayHi(String name) { + // throw new Error("Should not be called!"); + // } + // } + private static CommonClassDefinition INVALID_DEFINITION_T1 = new CommonClassDefinition( + Transform.class, + Base64.getDecoder().decode( + "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" + + "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" + + "aWxlAQARTm90VHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBu" + + "b3QgYmUgY2FsbGVkIQwABwAMAQAMTm90VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAgAAUA" + + "BgAAAAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwA" + + "AQAJAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO"), + Base64.getDecoder().decode( + "ZGV4CjAzNQDLV95i5xnv6iUi6uIeDoY5jP5Xe9NP1AiYAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAL" + + "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACQAQAACAEAAEoB" + + "AABSAQAAYgEAAHUBAACJAQAAnQEAALABAADHAQAAygEAAM4BAADiAQAAAQAAAAIAAAADAAAABAAA" + + "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" + + "AAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAPQBAAAAAAAAAQABAAEAAADpAQAABAAAAHAQAwAA" + + "AA4ABAACAAIAAADuAQAACQAAACIAAQAbAQYAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgAOTE5v" + + "dFRyYW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" + + "L2xhbmcvU3RyaW5nOwARTm90VHJhbnNmb3JtLmphdmEAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAB" + + "VgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAICABIgCAQGg" + + "AgAADAAAAAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAFAAAAnAAAAAMAAAACAAAAsAAAAAUA" + + "AAAEAAAAyAAAAAYAAAABAAAA6AAAAAEgAAACAAAACAEAAAEQAAABAAAARAEAAAIgAAALAAAASgEA" + + "AAMgAAACAAAA6QEAAAAgAAABAAAA9AEAAAAQAAABAAAABAIAAA==")); + + // Valid redefinition of Transform2 + // class Transform2 implements Iface1, Iface2 { + // public void sayHi(String name) { + // throw new Error("Should not be called!"); + // } + // } + private static CommonClassDefinition VALID_DEFINITION_T2 = new CommonClassDefinition( + Transform2.class, + Base64.getDecoder().decode( + "yv66vgAAADQAGQoABgARBwASCAATCgACABQHABUHABYHABcHABgBAAY8aW5pdD4BAAMoKVYBAARD" + + "b2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApT" + + "b3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91" + + "bGQgbm90IGJlIGNhbGxlZCEMAAkADgEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAG" + + "SWZhY2UxAQAGSWZhY2UyACAABQAGAAIABwAIAAAAAgAAAAkACgABAAsAAAAdAAEAAQAAAAUqtwAB" + + "sQAAAAEADAAAAAYAAQAAAAEAAQANAA4AAQALAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEADAAA" + + "AAYAAQAAAAMAAQAPAAAAAgAQ"), + Base64.getDecoder().decode( + "ZGV4CjAzNQDSWls05CPkX+gbTGMVRvx9dc9vozzVbu7AAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAN" + + "AAAAcAAAAAcAAACkAAAAAgAAAMAAAAAAAAAAAAAAAAQAAADYAAAAAQAAAPgAAACoAQAAGAEAAGIB" + + "AABqAQAAdAEAAH4BAACMAQAAnwEAALMBAADHAQAA3gEAAO8BAADyAQAA9gEAAAoCAAABAAAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAABcAQAAAgAAAAAAAAACAAEA" + + "DAAAAAMAAQAAAAAABAAAAAAAAAACAAAAAAAAAAQAAABUAQAACAAAAAAAAAAcAgAAAAAAAAEAAQAB" + + "AAAAEQIAAAQAAABwEAMAAAAOAAQAAgACAAAAFgIAAAkAAAAiAAMAGwEHAAAAcCACABAAJwAAAAIA" + + "AAAAAAEAAQAAAAUABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAxMVHJhbnNmb3JtMjsAEUxq" + + "YXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAV" + + "U2hvdWxkIG5vdCBiZSBjYWxsZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBq" + + "YWNrLTQuMjAABXNheUhpAAEABw4AAwEABw4AAAABAQCAgASYAgEBsAIAAAwAAAAAAAAAAQAAAAAA" + + "AAABAAAADQAAAHAAAAACAAAABwAAAKQAAAADAAAAAgAAAMAAAAAFAAAABAAAANgAAAAGAAAAAQAA" + + "APgAAAABIAAAAgAAABgBAAABEAAAAgAAAFQBAAACIAAADQAAAGIBAAADIAAAAgAAABECAAAAIAAA" + + "AQAAABwCAAAAEAAAAQAAACwCAAA=")); + + public static void doTest(Transform t1, Transform2 t2) { + t1.sayHi("MultiRetrans"); + t2.sayHi("MultiRetrans"); + try { + Main.addMultiTransformationResults(VALID_DEFINITION_T2, INVALID_DEFINITION_T1); + Main.enableCommonRetransformation(true); + Main.doCommonClassRetransformation(Transform2.class, Transform.class); + } catch (Exception e) { + System.out.println( + "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")"); + } finally { + Main.enableCommonRetransformation(false); + } + t1.sayHi("MultiRetrans"); + t2.sayHi("MultiRetrans"); + try { + Main.addMultiTransformationResults(VALID_DEFINITION_T2, INVALID_DEFINITION_T1); + Main.enableCommonRetransformation(true); + Main.doCommonClassRetransformation(Transform.class, Transform2.class); + } catch (Exception e) { + System.out.println( + "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")"); + } finally { + Main.enableCommonRetransformation(false); + } + t1.sayHi("MultiRetrans"); + t2.sayHi("MultiRetrans"); + } +} diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt index 54065223cf..3b7fb2408f 100644 --- a/test/924-threads/expected.txt +++ b/test/924-threads/expected.txt @@ -28,3 +28,6 @@ class dalvik.system.PathClassLoader e1 = ALIVE|WAITING_WITH_TIMEOUT|SLEEPING|WAITING 5 = ALIVE|RUNNABLE 2 = TERMINATED +[Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]] +JVMTI_ERROR_THREAD_NOT_ALIVE +JVMTI_ERROR_THREAD_NOT_ALIVE diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java index 048766604f..dec49a8320 100644 --- a/test/924-threads/src/Main.java +++ b/test/924-threads/src/Main.java @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.concurrent.CountDownLatch; import java.util.HashMap; import java.util.List; @@ -53,6 +54,10 @@ public class Main { printThreadInfo(t3); doStateTests(); + + doAllThreadsTests(); + + doTLSTests(); } private static class Holder { @@ -155,6 +160,80 @@ public class Main { printThreadState(t); } + private static void doAllThreadsTests() { + Thread[] threads = getAllThreads(); + Arrays.sort(threads, THREAD_COMP); + System.out.println(Arrays.toString(threads)); + } + + private static void doTLSTests() throws Exception { + doTLSNonLiveTests(); + doTLSLiveTests(); + } + + private static void doTLSNonLiveTests() throws Exception { + Thread t = new Thread(); + try { + setTLS(t, 1); + System.out.println("Expected failure setting TLS for non-live thread"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + t.start(); + t.join(); + try { + setTLS(t, 1); + System.out.println("Expected failure setting TLS for non-live thread"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + private static void doTLSLiveTests() throws Exception { + setTLS(Thread.currentThread(), 1); + + long l = getTLS(Thread.currentThread()); + if (l != 1) { + throw new RuntimeException("Unexpected TLS value: " + l); + }; + + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + Runnable r = new Runnable() { + @Override + public void run() { + try { + cdl1.countDown(); + cdl2.await(); + setTLS(Thread.currentThread(), 2); + if (getTLS(Thread.currentThread()) != 2) { + throw new RuntimeException("Different thread issue"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + + Thread t = new Thread(r); + t.start(); + cdl1.await(); + setTLS(Thread.currentThread(), 1); + cdl2.countDown(); + + t.join(); + if (getTLS(Thread.currentThread()) != 1) { + throw new RuntimeException("Got clobbered"); + } + } + + private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() { + public int compare(Thread o1, Thread o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>(); private final static List<Integer> STATE_KEYS = new ArrayList<Integer>(); static { @@ -213,4 +292,7 @@ public class Main { private static native Thread getCurrentThread(); private static native Object[] getThreadInfo(Thread t); private static native int getThreadState(Thread t); + private static native Thread[] getAllThreads(); + private static native void setTLS(Thread t, long l); + private static native long getTLS(Thread t); } diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc index 4abf8fcf93..d35eaa8365 100644 --- a/test/924-threads/threads.cc +++ b/test/924-threads/threads.cc @@ -100,5 +100,42 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getThreadState( return state; } +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllThreads( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jint thread_count; + jthread* threads; + + jvmtiError result = jvmti_env->GetAllThreads(&thread_count, &threads); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) { + return threads[index]; + }; + jobjectArray ret = CreateObjectArray(env, thread_count, "java/lang/Thread", callback); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads)); + + return ret; +} + +extern "C" JNIEXPORT jlong JNICALL Java_Main_getTLS( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) { + void* tls; + jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls); + if (JvmtiErrorToException(env, result)) { + return 0; + } + return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls)); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_setTLS( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) { + const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val)); + jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls); + JvmtiErrorToException(env, result); +} + } // namespace Test924Threads } // namespace art diff --git a/test/956-methodhandles/run b/test/925-threadgroups/build index a9f182288c..898e2e54a2 100755 --- a/test/956-methodhandles/run +++ b/test/925-threadgroups/build @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-build "$@" --experimental agents diff --git a/test/925-threadgroups/expected.txt b/test/925-threadgroups/expected.txt new file mode 100644 index 0000000000..7d1a259c89 --- /dev/null +++ b/test/925-threadgroups/expected.txt @@ -0,0 +1,16 @@ +java.lang.ThreadGroup[name=main,maxpri=10] + java.lang.ThreadGroup[name=system,maxpri=10] + main + 10 + false +java.lang.ThreadGroup[name=system,maxpri=10] + null + system + 10 + false +main: + [Thread[main,5,main]] + [] +system: + [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system]] + [java.lang.ThreadGroup[name=main,maxpri=10]] diff --git a/test/925-threadgroups/info.txt b/test/925-threadgroups/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/925-threadgroups/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/925-threadgroups/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java new file mode 100644 index 0000000000..c59efe2f7b --- /dev/null +++ b/test/925-threadgroups/src/Main.java @@ -0,0 +1,113 @@ +/* + * 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.util.Arrays; +import java.util.Comparator; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + Thread t1 = Thread.currentThread(); + ThreadGroup curGroup = t1.getThreadGroup(); + + ThreadGroup rootGroup = curGroup; + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + + ThreadGroup topGroups[] = getTopThreadGroups(); + if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) { + System.out.println(Arrays.toString(topGroups)); + throw new RuntimeException("Unexpected topGroups"); + } + + printThreadGroupInfo(curGroup); + printThreadGroupInfo(rootGroup); + + waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */); + + checkChildren(curGroup); + } + + private static void printThreadGroupInfo(ThreadGroup tg) { + Object[] threadGroupInfo = getThreadGroupInfo(tg); + if (threadGroupInfo == null || threadGroupInfo.length != 4) { + System.out.println(Arrays.toString(threadGroupInfo)); + throw new RuntimeException("threadGroupInfo length wrong"); + } + + System.out.println(tg); + System.out.println(" " + threadGroupInfo[0]); // Parent + System.out.println(" " + threadGroupInfo[1]); // Name + System.out.println(" " + threadGroupInfo[2]); // Priority + System.out.println(" " + threadGroupInfo[3]); // Daemon + } + + private static void checkChildren(ThreadGroup tg) { + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + ThreadGroup[] groups = (ThreadGroup[])data[1]; + + Arrays.sort(threads, THREAD_COMP); + Arrays.sort(groups, THREADGROUP_COMP); + System.out.println(tg.getName() + ":"); + System.out.println(" " + Arrays.toString(threads)); + System.out.println(" " + Arrays.toString(groups)); + + if (tg.getParent() != null) { + checkChildren(tg.getParent()); + } + } + + private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS) + throws Exception { + for (int i = 0; i < timeoutS; i++) { + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + if (threads.length == expectedChildCount) { + return; + } + Thread.sleep(1000); + } + + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + System.out.println(Arrays.toString(threads)); + throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children."); + } + + private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() { + public int compare(Thread o1, Thread o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() { + public int compare(ThreadGroup o1, ThreadGroup o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private static native ThreadGroup[] getTopThreadGroups(); + private static native Object[] getThreadGroupInfo(ThreadGroup tg); + // Returns an array where element 0 is an array of threads and element 1 is an array of groups. + private static native Object[] getThreadGroupChildren(ThreadGroup tg); +} diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc new file mode 100644 index 0000000000..6c6e835dd3 --- /dev/null +++ b/test/925-threadgroups/threadgroups.cc @@ -0,0 +1,127 @@ +/* + * 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 <stdio.h> + +#include "android-base/stringprintf.h" +#include "base/macros.h" +#include "base/logging.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedLocalRef.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test925ThreadGroups { + +// private static native Object[] getThreadGroupInfo(); +// // Returns an array where element 0 is an array of threads and element 1 is an array of groups. +// private static native Object[] getThreadGroupChildren(); + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTopThreadGroups( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jthreadGroup* groups; + jint group_count; + jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + return groups[index]; + }; + jobjectArray ret = CreateObjectArray(env, group_count, "java/lang/ThreadGroup", callback); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups)); + + return ret; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupInfo( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) { + jvmtiThreadGroupInfo info; + jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + switch (index) { + // The parent. + case 0: + return info.parent; + + // The name. + case 1: + return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name); + + // The priority. Use a string for simplicity of construction. + case 2: + return env->NewStringUTF(android::base::StringPrintf("%d", info.max_priority).c_str()); + + // Whether it's a daemon. Use a string for simplicity of construction. + case 3: + return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false"); + } + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + }; + return CreateObjectArray(env, 4, "java/lang/Object", callback); +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupChildren( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) { + jint thread_count; + jthread* threads; + jint threadgroup_count; + jthreadGroup* groups; + + jvmtiError result = jvmti_env->GetThreadGroupChildren(group, + &thread_count, + &threads, + &threadgroup_count, + &groups); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint component_index) -> jobject { + if (component_index == 0) { + // Threads. + auto inner_callback = [&](jint index) { + return threads[index]; + }; + return CreateObjectArray(env, thread_count, "java/lang/Thread", inner_callback); + } else { + // Groups. + auto inner_callback = [&](jint index) { + return groups[index]; + }; + return CreateObjectArray(env, threadgroup_count, "java/lang/ThreadGroup", inner_callback); + } + }; + jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups)); + + return ret; +} + +} // namespace Test925ThreadGroups +} // namespace art diff --git a/test/957-methodhandle-transforms/run b/test/926-multi-obsolescence/build index a9f182288c..898e2e54a2 100755 --- a/test/957-methodhandle-transforms/run +++ b/test/926-multi-obsolescence/build @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-build "$@" --experimental agents diff --git a/test/926-multi-obsolescence/expected.txt b/test/926-multi-obsolescence/expected.txt new file mode 100644 index 0000000000..0546490c44 --- /dev/null +++ b/test/926-multi-obsolescence/expected.txt @@ -0,0 +1,15 @@ +hello +hello - 2 +Not doing anything here +goodbye - 2 +goodbye +hello +hello - 2 +transforming calling functions +goodbye - 2 +goodbye +Hello - Transformed +Hello 2 - Transformed +Not doing anything here +Goodbye 2 - Transformed +Goodbye - Transformed diff --git a/test/926-multi-obsolescence/info.txt b/test/926-multi-obsolescence/info.txt new file mode 100644 index 0000000000..1399b966f1 --- /dev/null +++ b/test/926-multi-obsolescence/info.txt @@ -0,0 +1,2 @@ +Tests that we can redefine multiple classes at once using the RedefineClasses +function. diff --git a/test/926-multi-obsolescence/run b/test/926-multi-obsolescence/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/926-multi-obsolescence/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/926-multi-obsolescence/src/CommonClassDefinition.java b/test/926-multi-obsolescence/src/CommonClassDefinition.java new file mode 100644 index 0000000000..62602a02e9 --- /dev/null +++ b/test/926-multi-obsolescence/src/CommonClassDefinition.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } +} diff --git a/test/926-multi-obsolescence/src/Main.java b/test/926-multi-obsolescence/src/Main.java new file mode 100644 index 0000000000..8a6cf84b8b --- /dev/null +++ b/test/926-multi-obsolescence/src/Main.java @@ -0,0 +1,128 @@ +/* + * 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.ArrayList; +import java.util.Base64; + +public class Main { + // class Transform { + // public void sayHi(Runnable r) { + // System.out.println("Hello - Transformed"); + // r.run(); + // System.out.println("Goodbye - Transformed"); + // } + // } + private static CommonClassDefinition VALID_DEFINITION_T1 = new CommonClassDefinition( + Transform.class, + Base64.getDecoder().decode( + "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" + + "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" + + "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" + + "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" + + "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" + + "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" + + "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" + + "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" + + "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" + + "AQAPAAAAAgAQ"), + Base64.getDecoder().decode( + "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" + + "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" + + "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" + + "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" + + "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" + + "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" + + "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" + + "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" + + "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" + + "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" + + "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" + + "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" + + "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" + + "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" + + "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=")); + // class Transform2 { + // public void sayHi(Runnable r) { + // System.out.println("Hello 2 - Transformed"); + // r.run(); + // System.out.println("Goodbye 2 - Transformed"); + // } + // } + private static CommonClassDefinition VALID_DEFINITION_T2 = new CommonClassDefinition( + Transform2.class, + Base64.getDecoder().decode( + "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" + + "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" + + "KVYBAApTb3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoHABwMAB0AHgEAFUhlbGxvIDIg" + + "LSBUcmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABdHb29kYnllIDIgLSBUcmFuc2Zvcm1lZAEA" + + "ClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEA" + + "FUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAV" + + "KExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAA" + + "AAACAAAACQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsA" + + "AAA7AAIAAgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4A" + + "BQAWAAYAAQAPAAAAAgAQ"), + Base64.getDecoder().decode( + "ZGV4CjAzNQCee5Z6+AuFcjnPjjn7QYgZmKSmFQCO4nxUAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAR" + + "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAQAgAARAEAAKIB" + + "AACqAQAAwwEAANoBAADoAQAA/wEAABMCAAApAgAAPQIAAFECAABiAgAAZQIAAGkCAAB9AgAAggIA" + + "AIsCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" + + "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" + + "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" + + "AwAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" + + "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AF0dvb2RieWUgMiAtIFRyYW5zZm9ybWVkABVIZWxs" + + "byAyIC0gVHJhbnNmb3JtZWQADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJM" + + "amF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmlu" + + "ZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAPVHJhbnNmb3JtMi5qYXZhAAFWAAJWTAASZW1pdHRlcjog" + + "amFjay00LjIwAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICA" + + "BMQCAQHcAgANAAAAAAAAAAEAAAAAAAAAAQAAABEAAABwAAAAAgAAAAcAAAC0AAAAAwAAAAMAAADQ" + + "AAAABAAAAAEAAAD0AAAABQAAAAUAAAD8AAAABgAAAAEAAAAkAQAAASAAAAIAAABEAQAAARAAAAIA" + + "AACUAQAAAiAAABEAAACiAQAAAyAAAAIAAACXAgAAACAAAAEAAAClAgAAABAAAAEAAAC0AgAA")); + + public static void main(String[] args) { + System.loadLibrary(args[1]); + doTest(new Transform(), new Transform2()); + } + + public static void doTest(final Transform t1, final Transform2 t2) { + t1.sayHi(() -> { t2.sayHi(() -> { System.out.println("Not doing anything here"); }); }); + t1.sayHi(() -> { + t2.sayHi(() -> { + System.out.println("transforming calling functions"); + doMultiClassRedefinition(VALID_DEFINITION_T1, VALID_DEFINITION_T2); + }); + }); + t1.sayHi(() -> { t2.sayHi(() -> { System.out.println("Not doing anything here"); }); }); + } + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); +} diff --git a/test/926-multi-obsolescence/src/Transform.java b/test/926-multi-obsolescence/src/Transform.java new file mode 100644 index 0000000000..8cda6cdf53 --- /dev/null +++ b/test/926-multi-obsolescence/src/Transform.java @@ -0,0 +1,30 @@ +/* + * 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(Runnable r) { + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Hello" < "LTransform;" < "hello". + System.out.println("hello"); + r.run(); + System.out.println("goodbye"); + } +} diff --git a/test/926-multi-obsolescence/src/Transform2.java b/test/926-multi-obsolescence/src/Transform2.java new file mode 100644 index 0000000000..4877f8455b --- /dev/null +++ b/test/926-multi-obsolescence/src/Transform2.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 Transform2 { + public void sayHi(Runnable r) { + System.out.println("hello - 2"); + r.run(); + System.out.println("goodbye - 2"); + } +} diff --git a/test/954-invoke-polymorphic-verifier/run b/test/927-timers/build index a9f182288c..898e2e54a2 100755 --- a/test/954-invoke-polymorphic-verifier/run +++ b/test/927-timers/build @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-build "$@" --experimental agents diff --git a/test/927-timers/expected.txt b/test/927-timers/expected.txt new file mode 100644 index 0000000000..a4ef4424b6 --- /dev/null +++ b/test/927-timers/expected.txt @@ -0,0 +1,3 @@ +availableProcessors OK +[-1, true, true, 32] +Time OK diff --git a/test/927-timers/info.txt b/test/927-timers/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/927-timers/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/927-timers/run b/test/927-timers/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/927-timers/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/927-timers/src/Main.java b/test/927-timers/src/Main.java new file mode 100644 index 0000000000..2f5c85cab5 --- /dev/null +++ b/test/927-timers/src/Main.java @@ -0,0 +1,62 @@ +/* + * 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.util.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() { + int all1 = Runtime.getRuntime().availableProcessors(); + int all2 = getAvailableProcessors(); + if (all1 != all2) { + throw new RuntimeException("Available processors doesn't match: " + all1 + " vs " + all2); + } + System.out.println("availableProcessors OK"); + + Object info[] = getTimerInfo(); + System.out.println(Arrays.toString(info)); + + // getTime checks. + // Note: there isn't really much to check independent from the implementation. So we check + // a few details of the ART implementation. This may fail on other runtimes. + long time1 = getTime(); + long time2 = getTime(); + + // Under normal circumstances, time1 <= time2. + if (time2 < time1) { + throw new RuntimeException("Time unexpectedly decreased: " + time1 + " vs " + time2); + } + + long time3 = System.nanoTime(); + long time4 = getTime(); + + final long MINUTE = 60l * 1000 * 1000 * 1000; + if (time4 < time3 || (time4 - time3 > MINUTE)) { + throw new RuntimeException("Time unexpectedly divergent: " + time3 + " vs " + time4); + } + + System.out.println("Time OK"); + } + + private static native int getAvailableProcessors(); + private static native Object[] getTimerInfo(); + private static native long getTime(); +} diff --git a/test/927-timers/timers.cc b/test/927-timers/timers.cc new file mode 100644 index 0000000000..58d5c271e6 --- /dev/null +++ b/test/927-timers/timers.cc @@ -0,0 +1,84 @@ +/* + * 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 <inttypes.h> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test926Timers { + +extern "C" JNIEXPORT jint JNICALL Java_Main_getAvailableProcessors( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jint count; + jvmtiError result = jvmti_env->GetAvailableProcessors(&count); + if (JvmtiErrorToException(env, result)) { + return -1; + } + return count; +} + +extern "C" JNIEXPORT jlong JNICALL Java_Main_getTime( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jlong time; + jvmtiError result = jvmti_env->GetTime(&time); + if (JvmtiErrorToException(env, result)) { + return -1; + } + return time; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTimerInfo( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jvmtiTimerInfo info; + jvmtiError result = jvmti_env->GetTimerInfo(&info); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + switch (index) { + // Max value. + case 0: + return env->NewStringUTF(android::base::StringPrintf("%" PRId64, info.max_value).c_str()); + + // Skip forward. + case 1: + return env->NewStringUTF(info.may_skip_forward == JNI_TRUE ? "true" : "false"); + // Skip backward. + case 2: + return env->NewStringUTF(info.may_skip_forward == JNI_TRUE ? "true" : "false"); + + // The kind. + case 3: + return env->NewStringUTF( + android::base::StringPrintf("%d", static_cast<jint>(info.kind)).c_str()); + } + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + }; + return CreateObjectArray(env, 4, "java/lang/Object", callback); +} + +} // namespace Test926Timers +} // namespace art diff --git a/test/955-methodhandles-smali/run b/test/928-jni-table/build index a9f182288c..898e2e54a2 100755 --- a/test/955-methodhandles-smali/run +++ b/test/928-jni-table/build @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-build "$@" --experimental agents diff --git a/test/928-jni-table/expected.txt b/test/928-jni-table/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/928-jni-table/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/928-jni-table/info.txt b/test/928-jni-table/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/928-jni-table/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc new file mode 100644 index 0000000000..5123d3a43f --- /dev/null +++ b/test/928-jni-table/jni_table.cc @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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 <stdio.h> + +#include "jni.h" +#include "openjdkjvmti/jvmti.h" + +#include "base/logging.h" +#include "base/macros.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test927JNITable { + +// This test is equivalent to the jni_internal_test JNIEnvExtTableOverride. + +static size_t gGlobalRefCount = 0; +static JNINativeInterface* gOriginalEnv = nullptr; + +static jobject CountNewGlobalRef(JNIEnv* env, jobject o) { + ++gGlobalRefCount; + return gOriginalEnv->NewGlobalRef(env, o); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest( + JNIEnv* env, jclass klass) { + // Get the current table, as the delegate. + jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv); + if (JvmtiErrorToException(env, getorig_result)) { + return; + } + + // Get the current table, as the override we'll install. + JNINativeInterface* env_override; + jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override); + if (JvmtiErrorToException(env, getoverride_result)) { + return; + } + + env_override->NewGlobalRef = CountNewGlobalRef; + gGlobalRefCount = 0; + + // Install the override. + jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override); + if (JvmtiErrorToException(env, setoverride_result)) { + return; + } + + jobject global = env->NewGlobalRef(klass); + CHECK_EQ(1u, gGlobalRefCount); + env->DeleteGlobalRef(global); + + // Install the "original." There is no real reset. + jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv); + if (JvmtiErrorToException(env, setoverride2_result)) { + return; + } + + jobject global2 = env->NewGlobalRef(klass); + CHECK_EQ(1u, gGlobalRefCount); + env->DeleteGlobalRef(global2); + + // Try to install null. Should return NULL_POINTER error. + jvmtiError setoverride3_result = jvmti_env->SetJNIFunctionTable(nullptr); + if (setoverride3_result != JVMTI_ERROR_NULL_POINTER) { + LOG(FATAL) << "Didn't receive NULL_POINTER"; + } + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(env_override)); +} + +} // namespace Test927JNITable +} // namespace art diff --git a/test/928-jni-table/run b/test/928-jni-table/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/928-jni-table/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java new file mode 100644 index 0000000000..b0baea1f9d --- /dev/null +++ b/test/928-jni-table/src/Main.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doJNITableTest(); + + System.out.println("Done"); + } + + public static native void doJNITableTest(); +} diff --git a/test/929-search/build b/test/929-search/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/929-search/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/929-search/expected.txt b/test/929-search/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/929-search/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/929-search/info.txt b/test/929-search/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/929-search/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/929-search/run b/test/929-search/run new file mode 100755 index 0000000000..0a8d0672f6 --- /dev/null +++ b/test/929-search/run @@ -0,0 +1,23 @@ +#!/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. + +# 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 \ + --no-app-image diff --git a/test/929-search/search.cc b/test/929-search/search.cc new file mode 100644 index 0000000000..d1c698491e --- /dev/null +++ b/test/929-search/search.cc @@ -0,0 +1,53 @@ +/* + * 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 <inttypes.h> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedUtfChars.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test929Search { + +extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) { + ScopedUtfChars utf(env, segment); + if (utf.c_str() == nullptr) { + return; + } + jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str()); + JvmtiErrorToException(env, result); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) { + ScopedUtfChars utf(env, segment); + if (utf.c_str() == nullptr) { + return; + } + jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str()); + JvmtiErrorToException(env, result); +} + +} // namespace Test929Search +} // namespace art diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java new file mode 100644 index 0000000000..64acb2fcfe --- /dev/null +++ b/test/929-search/src-ex/A.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class A { +}
\ No newline at end of file diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java new file mode 100644 index 0000000000..f1458c3bca --- /dev/null +++ b/test/929-search/src/B.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class B { +}
\ No newline at end of file diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java new file mode 100644 index 0000000000..d253e6fdf6 --- /dev/null +++ b/test/929-search/src/Main.java @@ -0,0 +1,54 @@ +/* + * 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.util.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + doTest(true, DEX1, "B"); + doTest(false, DEX2, "A"); + System.out.println("Done"); + } + + private static void doTest(boolean boot, String segment, String className) throws Exception { + ClassLoader expectedClassLoader; + if (boot) { + expectedClassLoader = Object.class.getClassLoader(); + addToBootClassLoader(segment); + } else { + expectedClassLoader = ClassLoader.getSystemClassLoader(); + addToSystemClassLoader(segment); + } + + Class<?> c = Class.forName(className); + if (c.getClassLoader() != expectedClassLoader) { + throw new RuntimeException(className + "(" + boot + "/" + segment + "): " + + c.getClassLoader() + " vs " + expectedClassLoader); + } + } + + private static native void addToBootClassLoader(String s); + private static native void addToSystemClassLoader(String s); + + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar"; + private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar"; +} diff --git a/test/930-hello-retransform/build b/test/930-hello-retransform/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/930-hello-retransform/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/930-hello-retransform/expected.txt b/test/930-hello-retransform/expected.txt new file mode 100644 index 0000000000..4774b81b49 --- /dev/null +++ b/test/930-hello-retransform/expected.txt @@ -0,0 +1,2 @@ +hello +Goodbye diff --git a/test/930-hello-retransform/info.txt b/test/930-hello-retransform/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/930-hello-retransform/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/930-hello-retransform/run b/test/930-hello-retransform/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/930-hello-retransform/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/930-hello-retransform/src/Main.java b/test/930-hello-retransform/src/Main.java new file mode 100644 index 0000000000..12194c3235 --- /dev/null +++ b/test/930-hello-retransform/src/Main.java @@ -0,0 +1,70 @@ +/* + * 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) { + System.loadLibrary(args[1]); + doTest(new Transform()); + } + + public static void doTest(Transform t) { + t.sayHi(); + addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class); + t.sayHi(); + } + + // Transforms the class + private static native void doCommonClassRetransformation(Class<?>... target); + 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/930-hello-retransform/src/Transform.java b/test/930-hello-retransform/src/Transform.java new file mode 100644 index 0000000000..8e8af355da --- /dev/null +++ b/test/930-hello-retransform/src/Transform.java @@ -0,0 +1,28 @@ +/* + * 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() { + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Goodbye" < "LTransform;" < "hello". + System.out.println("hello"); + } +} diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc new file mode 100644 index 0000000000..6ace4cea68 --- /dev/null +++ b/test/931-agent-thread/agent_thread.cc @@ -0,0 +1,132 @@ +/* + * 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 <inttypes.h> + +#include "barrier.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "runtime.h" +#include "ScopedLocalRef.h" +#include "thread-inl.h" +#include "well_known_classes.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test930AgentThread { + +struct AgentData { + AgentData() : main_thread(nullptr), + jvmti_env(nullptr), + b(2) { + } + + jthread main_thread; + jvmtiEnv* jvmti_env; + Barrier b; + jint priority; +}; + +static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) { + AgentData* data = reinterpret_cast<AgentData*>(arg); + + // Check some basics. + // This thread is not the main thread. + jthread this_thread; + jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread); + CHECK(!JvmtiErrorToException(env, this_thread_result)); + CHECK(!env->IsSameObject(this_thread, data->main_thread)); + + // The thread is a daemon. + jvmtiThreadInfo info; + jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info); + CHECK(!JvmtiErrorToException(env, info_result)); + CHECK(info.is_daemon); + + // The thread has the requested priority. + // TODO: Our thread priorities do not work on the host. + // CHECK_EQ(info.priority, data->priority); + + // Check further parts of the thread: + jint thread_count; + jthread* threads; + jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads); + CHECK(!JvmtiErrorToException(env, threads_result)); + bool found = false; + for (jint i = 0; i != thread_count; ++i) { + if (env->IsSameObject(threads[i], this_thread)) { + found = true; + break; + } + } + CHECK(found); + + // Done, let the main thread progress. + data->b.Pass(Thread::Current()); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + // Create a Thread object. + ScopedLocalRef<jobject> thread_name(env, + env->NewStringUTF("Agent Thread")); + if (thread_name.get() == nullptr) { + return; + } + + ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread)); + if (thread.get() == nullptr) { + return; + } + + env->CallNonvirtualVoidMethod(thread.get(), + WellKnownClasses::java_lang_Thread, + WellKnownClasses::java_lang_Thread_init, + Runtime::Current()->GetMainThreadGroup(), + thread_name.get(), + kMinThreadPriority, + JNI_FALSE); + if (env->ExceptionCheck()) { + return; + } + + jthread main_thread; + jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread); + if (JvmtiErrorToException(env, main_thread_result)) { + return; + } + + AgentData data; + data.main_thread = env->NewGlobalRef(main_thread); + data.jvmti_env = jvmti_env; + data.priority = JVMTI_THREAD_MIN_PRIORITY; + + jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority); + if (JvmtiErrorToException(env, result)) { + return; + } + + data.b.Wait(Thread::Current()); + + env->DeleteGlobalRef(data.main_thread); +} + +} // namespace Test930AgentThread +} // namespace art diff --git a/test/931-agent-thread/build b/test/931-agent-thread/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/931-agent-thread/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/931-agent-thread/expected.txt b/test/931-agent-thread/expected.txt new file mode 100644 index 0000000000..a965a70ed4 --- /dev/null +++ b/test/931-agent-thread/expected.txt @@ -0,0 +1 @@ +Done diff --git a/test/931-agent-thread/info.txt b/test/931-agent-thread/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/931-agent-thread/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/931-agent-thread/run b/test/931-agent-thread/run new file mode 100755 index 0000000000..0a8d0672f6 --- /dev/null +++ b/test/931-agent-thread/run @@ -0,0 +1,23 @@ +#!/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. + +# 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 \ + --no-app-image diff --git a/test/931-agent-thread/src/Main.java b/test/931-agent-thread/src/Main.java new file mode 100644 index 0000000000..6471bc8437 --- /dev/null +++ b/test/931-agent-thread/src/Main.java @@ -0,0 +1,29 @@ +/* + * 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.util.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + testAgentThread(); + + System.out.println("Done"); + } + + private static native void testAgentThread(); +} diff --git a/test/932-transform-saves/build b/test/932-transform-saves/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/932-transform-saves/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/932-transform-saves/expected.txt b/test/932-transform-saves/expected.txt new file mode 100644 index 0000000000..5097771994 --- /dev/null +++ b/test/932-transform-saves/expected.txt @@ -0,0 +1,3 @@ +hello +Goodbye +hello diff --git a/test/932-transform-saves/info.txt b/test/932-transform-saves/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/932-transform-saves/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/932-transform-saves/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/932-transform-saves/src/Main.java b/test/932-transform-saves/src/Main.java new file mode 100644 index 0000000000..d98ba6dbff --- /dev/null +++ b/test/932-transform-saves/src/Main.java @@ -0,0 +1,116 @@ +/* + * 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("hello"); + * } + * } + */ + private static final byte[] CLASS_BYTES_A = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" + + "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" + + "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAA" + + "AAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" + + "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN"); + private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode( + "ZGV4CjAzNQC6XWInnnDd1H4NdQ3P3inH8eCVmQI6W7LMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" + + "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" + + "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" + + "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" + + "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjIABWhlbGxvAANvdXQA" + + "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCAgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA="); + + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES_B = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" + + "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" + + "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" + + "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" + + "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0="); + private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + public static void main(String[] args) { + System.loadLibrary(args[1]); + doTest(new Transform()); + } + + public static void doTest(Transform t) { + // TODO We currently need to do this transform call since we don't have any way to make the + // original-dex-file a single-class dex-file letting us restore it easily. We should use the + // manipulation library that is being made when we store the original dex file. + // TODO REMOVE this theoretically does nothing but it ensures the original-dex-file we have set + // is one we can return to unaltered. + doCommonClassRedefinition(Transform.class, CLASS_BYTES_A, DEX_BYTES_A); + t.sayHi(); + + // Now turn it into DEX_BYTES_B so it says 'Goodbye' + addCommonTransformationResult("Transform", CLASS_BYTES_B, DEX_BYTES_B); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class); + t.sayHi(); + + // Now turn it back to normal by removing the load-hook and transforming again. + enableCommonRetransformation(false); + doCommonClassRetransformation(Transform.class); + t.sayHi(); + } + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_bytes, + byte[] dex_bytes); + private static native void doCommonClassRetransformation(Class<?>... target); + 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/932-transform-saves/src/Transform.java b/test/932-transform-saves/src/Transform.java new file mode 100644 index 0000000000..8e8af355da --- /dev/null +++ b/test/932-transform-saves/src/Transform.java @@ -0,0 +1,28 @@ +/* + * 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() { + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Goodbye" < "LTransform;" < "hello". + System.out.println("hello"); + } +} diff --git a/test/953-invoke-polymorphic-compiler/build b/test/953-invoke-polymorphic-compiler/build new file mode 100755 index 0000000000..a423ca6b4e --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/build @@ -0,0 +1,25 @@ +#!/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. + +# make us exit on a failure +set -e + +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm. + export USE_JACK=true +fi + +./default-build "$@" --experimental method-handles diff --git a/test/953-invoke-polymorphic-compiler/expected.txt b/test/953-invoke-polymorphic-compiler/expected.txt new file mode 100644 index 0000000000..f47ee23fd8 --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/expected.txt @@ -0,0 +1,25 @@ +Running Main.Min2Print2([33, -4]) +Running Main.Min2Print2([-4, 33]) +Running Main.Min2Print3([33, -4, 17]) +Running Main.Min2Print3([-4, 17, 33]) +Running Main.Min2Print3([17, 33, -4]) +Running Main.Min2Print6([33, -4, 77, 88, 99, 111]) +Running Main.Min2Print6([-4, 77, 88, 99, 111, 33]) +Running Main.Min2Print6([77, 88, 99, 111, 33, -4]) +Running Main.Min2Print6([88, 99, 111, 33, -4, 77]) +Running Main.Min2Print6([99, 111, 33, -4, 77, 88]) +Running Main.Min2Print6([111, 33, -4, 77, 88, 99]) +Running Main.Min2Print26([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]) +Running Main.Min2Print26([25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]) +Running Main.Min2Print26([24, 25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]) +BasicTest done. +$opt$ReturnBooleanTest done. +$opt$ReturnCharTest done. +$opt$ReturnByteTest done. +$opt$ReturnShortTest done. +$opt$ReturnIntTest done. +$opt$ReturnLongTest done. +$opt$ReturnFloatTest done. +$opt$ReturnDoubleTest done. +$opt$ReturnStringTest done. +ReturnValuesTest done. diff --git a/test/953-invoke-polymorphic-compiler/info.txt b/test/953-invoke-polymorphic-compiler/info.txt new file mode 100644 index 0000000000..f1dbb61640 --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/info.txt @@ -0,0 +1,3 @@ +Tests for method handle invocations. + +NOTE: needs to run under ART or a Java 8 Language runtime and compiler. diff --git a/test/953-invoke-polymorphic-compiler/src/Main.java b/test/953-invoke-polymorphic-compiler/src/Main.java new file mode 100644 index 0000000000..20a8fec112 --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/src/Main.java @@ -0,0 +1,374 @@ +/* + * 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.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Main { + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertEquals(int i1, int i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + public static void assertEquals(long i1, long i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals l1: " + i1 + ", l2: " + i2); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; + } + + if (s1 != null && s2 != null && s1.equals(s2)) { + return; + } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } + + public static void fail() { + System.err.println("fail"); + Thread.dumpStack(); + } + + public static void fail(String message) { + System.err.println("fail: " + message); + Thread.dumpStack(); + } + + public static int Min2Print2(int a, int b) { + int[] values = new int[] { a, b }; + System.err.println("Running Main.Min2Print2(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static int Min2Print3(int a, int b, int c) { + int[] values = new int[] { a, b, c }; + System.err.println("Running Main.Min2Print3(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static int Min2Print6(int a, int b, int c, int d, int e, int f) { + int[] values = new int[] { a, b, c, d, e, f }; + System.err.println("Running Main.Min2Print6(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static int Min2Print26(int a, int b, int c, int d, + int e, int f, int g, int h, + int i, int j, int k, int l, + int m, int n, int o, int p, + int q, int r, int s, int t, + int u, int v, int w, int x, + int y, int z) { + int[] values = new int[] { a, b, c, d, e, f, g, h, i, j, k, l, m, + n, o, p, q, r, s, t, u, v, w, x, y, z }; + System.err.println("Running Main.Min2Print26(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static void $opt$BasicTest() throws Throwable { + MethodHandle mh; + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print2", MethodType.methodType(int.class, int.class, int.class)); + assertEquals((int) mh.invokeExact(33, -4), 33); + assertEquals((int) mh.invokeExact(-4, 33), 33); + + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print3", + MethodType.methodType(int.class, int.class, int.class, int.class)); + assertEquals((int) mh.invokeExact(33, -4, 17), 33); + assertEquals((int) mh.invokeExact(-4, 17, 33), 17); + assertEquals((int) mh.invokeExact(17, 33, -4), 33); + + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print6", + MethodType.methodType( + int.class, int.class, int.class, int.class, int.class, int.class, int.class)); + assertEquals((int) mh.invokeExact(33, -4, 77, 88, 99, 111), 33); + try { + // Too few arguments + assertEquals((int) mh.invokeExact(33, -4, 77, 88), 33); + fail("No WMTE for too few arguments"); + } catch (WrongMethodTypeException e) {} + try { + // Too many arguments + assertEquals((int) mh.invokeExact(33, -4, 77, 88, 89, 90, 91), 33); + fail("No WMTE for too many arguments"); + } catch (WrongMethodTypeException e) {} + assertEquals((int) mh.invokeExact(-4, 77, 88, 99, 111, 33), 77); + assertEquals((int) mh.invokeExact(77, 88, 99, 111, 33, -4), 88); + assertEquals((int) mh.invokeExact(88, 99, 111, 33, -4, 77), 99); + assertEquals((int) mh.invokeExact(99, 111, 33, -4, 77, 88), 111); + assertEquals((int) mh.invokeExact(111, 33, -4, 77, 88, 99), 111); + + // A preposterous number of arguments. + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print26", + MethodType.methodType( + // Return-type + int.class, + // Arguments + int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, + int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, + int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, + int.class, int.class)); + assertEquals(1, (int) mh.invokeExact(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)); + assertEquals(25, (int) mh.invokeExact(25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24)); + assertEquals(25, (int) mh.invokeExact(24, 25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)); + + try { + // Wrong argument type + mh.invokeExact("a"); + fail("No WMTE for wrong arguments"); + } catch (WrongMethodTypeException wmte) {} + + try { + // Invoke on null handle. + MethodHandle mh0 = null; + mh0.invokeExact("bad"); + fail("No NPE for you"); + } catch (NullPointerException npe) {} + + System.err.println("BasicTest done."); + } + + private static boolean And(boolean lhs, boolean rhs) { + return lhs & rhs; + } + + private static boolean Xor(boolean lhs, boolean rhs) { + return lhs ^ rhs; + } + + private static String Multiply(String value, int n) { + String result = ""; + for (int i = 0; i < n; ++i) { + result = value + result; + } + return result; + } + + private static byte Multiply(byte value, byte n) { + return (byte)(value * n); + } + + private static short Multiply(short value, short n) { + return (short)(value * n); + } + + private static int Multiply(int value, int n) { + return value * n; + } + + private static long Multiply(long value, long n) { + return value * n; + } + + private static float Multiply(float value, float n) { + return value * n; + } + + private static double Multiply(double value, double n) { + return value * n; + } + + private static char Next(char c) { + return (char)(c + 1); + } + + public static void $opt$ReturnBooleanTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = + lookup.findStatic(Main.class, "And", + MethodType.methodType(boolean.class, boolean.class, boolean.class)); + assertEquals(true, (boolean) mh.invokeExact(true, true)); + assertEquals(false, (boolean) mh.invokeExact(true, false)); + assertEquals(false, (boolean) mh.invokeExact(false, true)); + assertEquals(false, (boolean) mh.invokeExact(false, false)); + assertEquals(true, (boolean) mh.invoke(true, true)); + assertEquals(false, (boolean) mh.invoke(true, false)); + assertEquals(false, (boolean) mh.invoke(false, true)); + assertEquals(false, (boolean) mh.invoke(false, false)); + + mh = lookup.findStatic(Main.class, "Xor", + MethodType.methodType(boolean.class, boolean.class, boolean.class)); + assertEquals(false, (boolean) mh.invokeExact(true, true)); + assertEquals(true, (boolean) mh.invokeExact(true, false)); + assertEquals(true, (boolean) mh.invokeExact(false, true)); + assertEquals(false, (boolean) mh.invokeExact(false, false)); + assertEquals(false, (boolean) mh.invoke(true, true)); + assertEquals(true, (boolean) mh.invoke(true, false)); + assertEquals(true, (boolean) mh.invoke(false, true)); + assertEquals(false, (boolean) mh.invoke(false, false)); + + System.err.println("$opt$ReturnBooleanTest done."); + } + + public static void $opt$ReturnCharTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Next", + MethodType.methodType(char.class, char.class)); + assertEquals('B', (char) mh.invokeExact('A')); + assertEquals((char) -55, (char) mh.invokeExact((char) -56)); + System.err.println("$opt$ReturnCharTest done."); + } + + public static void $opt$ReturnByteTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(byte.class, byte.class, byte.class)); + assertEquals((byte) 30, (byte) mh.invokeExact((byte) 10, (byte) 3)); + assertEquals((byte) -90, (byte) mh.invoke((byte) -10, (byte) 9)); + System.err.println("$opt$ReturnByteTest done."); + } + + public static void $opt$ReturnShortTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(short.class, short.class, short.class)); + assertEquals((short) 3000, (short) mh.invokeExact((short) 1000, (short) 3)); + assertEquals((short) -3000, (short) mh.invoke((short) -1000, (short) 3)); + System.err.println("$opt$ReturnShortTest done."); + } + + public static void $opt$ReturnIntTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(int.class, int.class, int.class)); + assertEquals(3_000_000, (int) mh.invokeExact(1_000_000, 3)); + assertEquals(-3_000_000, (int) mh.invoke(-1_000, 3_000)); + System.err.println("$opt$ReturnIntTest done."); + } + + public static void $opt$ReturnLongTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(long.class, long.class, long.class)); + assertEquals(4_294_967_295_000L, (long) mh.invokeExact(1000L, 4_294_967_295L)); + assertEquals(-4_294_967_295_000L, (long) mh.invoke(-1000L, 4_294_967_295L)); + System.err.println("$opt$ReturnLongTest done."); + } + + public static void $opt$ReturnFloatTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(float.class, float.class, float.class)); + assertEquals(3.0F, (float) mh.invokeExact(1000.0F, 3e-3F)); + assertEquals(-3.0F, (float) mh.invoke(-1000.0F, 3e-3F)); + System.err.println("$opt$ReturnFloatTest done."); + } + + public static void $opt$ReturnDoubleTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(double.class, double.class, double.class)); + assertEquals(3033000.0, (double) mh.invokeExact(1000.0, 3.033e3)); + assertEquals(-3033000.0, (double) mh.invoke(-1000.0, 3.033e3)); + System.err.println("$opt$ReturnDoubleTest done."); + } + + public static void $opt$ReturnStringTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(String.class, String.class, int.class)); + assertEquals("100010001000", (String) mh.invokeExact("1000", 3)); + assertEquals("100010001000", (String) mh.invoke("1000", 3)); + System.err.println("$opt$ReturnStringTest done."); + } + + public static void ReturnValuesTest() throws Throwable { + $opt$ReturnBooleanTest(); + $opt$ReturnCharTest(); + $opt$ReturnByteTest(); + $opt$ReturnShortTest(); + $opt$ReturnIntTest(); + $opt$ReturnLongTest(); + $opt$ReturnFloatTest(); + $opt$ReturnDoubleTest(); + $opt$ReturnStringTest(); + System.err.println("ReturnValuesTest done."); + } + + static class ValueHolder { + public boolean m_z; + public static boolean s_z; + } + + public static void $opt$AccessorsTest() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + MethodHandle setMember = lookup.findSetter(ValueHolder.class, "m_z", boolean.class); + MethodHandle getMember = lookup.findGetter(ValueHolder.class, "m_z", boolean.class); + MethodHandle setStatic = lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class); + MethodHandle getStatic = lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class); + + boolean [] values = { false, true, false, true, false }; + for (boolean value : values) { + assertEquals((boolean) getStatic.invoke(), ValueHolder.s_z); + setStatic.invoke(value); + ValueHolder.s_z = value; + assertEquals(ValueHolder.s_z, value); + assertEquals((boolean) getStatic.invoke(), value); + + assertEquals((boolean) getMember.invoke(valueHolder), valueHolder.m_z); + setMember.invoke(valueHolder, value); + valueHolder.m_z = value; + assertEquals(valueHolder.m_z, value); + assertEquals((boolean) getMember.invoke(valueHolder), value); + } + } + + public static void main(String[] args) throws Throwable { + $opt$BasicTest(); + ReturnValuesTest(); + $opt$AccessorsTest(); + } +} diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index 17b56b4fd9..f8daba6239 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -76,6 +76,7 @@ public class Main { testStringConstructors(); testReturnValueConversions(); testVariableArity(); + testVariableArity_MethodHandles_bind(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -1466,4 +1467,23 @@ public class Main { fail(); } catch (WrongMethodTypeException e) {} } + + // The same tests as the above, except that we use use MethodHandles.bind instead of + // MethodHandle.bindTo. + public static void testVariableArity_MethodHandles_bind() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + MethodHandle mh = MethodHandles.lookup().bind(vat, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + + assertEquals("[]", mh.invoke()); + assertEquals("[true, false, true]", mh.invoke(true, false, true)); + assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true})); + assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true))); + + try { + mh.invoke(true, true, 0); + fail(); + } catch (WrongMethodTypeException e) {} + } } diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index 5806509fc3..eebf55fb61 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -33,6 +33,7 @@ public class Main { testBindTo(); testFilterReturnValue(); testPermuteArguments(); + testInvokers(); } public static void testThrowException() throws Throwable { @@ -40,17 +41,17 @@ public class Main { IllegalArgumentException.class); if (handle.type().returnType() != String.class) { - System.out.println("Unexpected return type for handle: " + handle + + fail("Unexpected return type for handle: " + handle + " [ " + handle.type() + "]"); } final IllegalArgumentException iae = new IllegalArgumentException("boo!"); try { handle.invoke(iae); - System.out.println("Expected an exception of type: java.lang.IllegalArgumentException"); + fail("Expected an exception of type: java.lang.IllegalArgumentException"); } catch (IllegalArgumentException expected) { if (expected != iae) { - System.out.println("Wrong exception: expected " + iae + " but was " + expected); + fail("Wrong exception: expected " + iae + " but was " + expected); } } } @@ -262,7 +263,7 @@ public class Main { array[0] = 42; int value = (int) getter.invoke(array, 0); if (value != 42) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } try { @@ -284,7 +285,7 @@ public class Main { array[0] = 42; long value = (long) getter.invoke(array, 0); if (value != 42l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -294,7 +295,7 @@ public class Main { array[0] = 42; short value = (short) getter.invoke(array, 0); if (value != 42l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -304,7 +305,7 @@ public class Main { array[0] = 42; char value = (char) getter.invoke(array, 0); if (value != 42l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -314,7 +315,7 @@ public class Main { array[0] = (byte) 0x8; byte value = (byte) getter.invoke(array, 0); if (value != (byte) 0x8) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -324,7 +325,7 @@ public class Main { array[0] = true; boolean value = (boolean) getter.invoke(array, 0); if (!value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -334,7 +335,7 @@ public class Main { array[0] = 42.0f; float value = (float) getter.invoke(array, 0); if (value != 42.0f) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -344,7 +345,7 @@ public class Main { array[0] = 42.0; double value = (double) getter.invoke(array, 0); if (value != 42.0) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -372,10 +373,10 @@ public class Main { setter.invoke(array, 1, 43); if (array[0] != 42) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } if (array[1] != 43) { - System.out.println("Unexpected value: " + array[1]); + fail("Unexpected value: " + array[1]); } try { @@ -396,7 +397,7 @@ public class Main { long[] array = new long[1]; setter.invoke(array, 0, 42l); if (array[0] != 42l) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -405,7 +406,7 @@ public class Main { short[] array = new short[1]; setter.invoke(array, 0, (short) 42); if (array[0] != 42l) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -414,7 +415,7 @@ public class Main { char[] array = new char[1]; setter.invoke(array, 0, (char) 42); if (array[0] != 42) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -423,7 +424,7 @@ public class Main { byte[] array = new byte[1]; setter.invoke(array, 0, (byte) 0x8); if (array[0] != (byte) 0x8) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -432,7 +433,7 @@ public class Main { boolean[] array = new boolean[1]; setter.invoke(array, 0, true); if (!array[0]) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -441,7 +442,7 @@ public class Main { float[] array = new float[1]; setter.invoke(array, 0, 42.0f); if (array[0] != 42.0f) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -450,7 +451,7 @@ public class Main { double[] array = new double[1]; setter.invoke(array, 0, 42.0); if (array[0] != 42.0) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -471,7 +472,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(boolean.class); boolean value = (boolean) identity.invoke(false); if (value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -479,7 +480,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(byte.class); byte value = (byte) identity.invoke((byte) 0x8); if (value != (byte) 0x8) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -487,7 +488,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(char.class); char value = (char) identity.invoke((char) -56); if (value != (char) -56) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -495,7 +496,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(short.class); short value = (short) identity.invoke((short) -59); if (value != (short) -59) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + Short.toString(value)); } } @@ -503,7 +504,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(int.class); int value = (int) identity.invoke(52); if (value != 52) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -511,7 +512,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(long.class); long value = (long) identity.invoke(-76l); if (value != (long) -76) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -519,7 +520,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(float.class); float value = (float) identity.invoke(56.0f); if (value != (float) 56.0f) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -527,7 +528,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(double.class); double value = (double) identity.invoke((double) 72.0); if (value != (double) 72.0) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -544,28 +545,28 @@ public class Main { MethodHandle constant = MethodHandles.constant(int.class, 56); int value = (int) constant.invoke(); if (value != 56) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // short constant values are converted to int. constant = MethodHandles.constant(int.class, (short) 52); value = (int) constant.invoke(); if (value != 52) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // char constant values are converted to int. constant = MethodHandles.constant(int.class, (char) 'b'); value = (int) constant.invoke(); if (value != (int) 'b') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // int constant values are converted to int. constant = MethodHandles.constant(int.class, (byte) 0x1); value = (int) constant.invoke(); if (value != 1) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // boolean, float, double and long primitive constants are not convertible @@ -600,13 +601,13 @@ public class Main { MethodHandle constant = MethodHandles.constant(long.class, 56l); long value = (long) constant.invoke(); if (value != 56l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } constant = MethodHandles.constant(long.class, (int) 56); value = (long) constant.invoke(); if (value != 56l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -615,7 +616,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); byte value = (byte) constant.invoke(); if (value != (byte) 0x12) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -624,7 +625,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(boolean.class, true); boolean value = (boolean) constant.invoke(); if (!value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -633,7 +634,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(char.class, 'f'); char value = (char) constant.invoke(); if (value != 'f') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -642,7 +643,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(short.class, (short) 123); short value = (short) constant.invoke(); if (value != (short) 123) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -651,7 +652,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(float.class, 56.0f); float value = (float) constant.invoke(); if (value != 56.0f) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -660,7 +661,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(double.class, 256.0); double value = (double) constant.invoke(); if (value != 256.0) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -678,13 +679,13 @@ public class Main { char value = (char) stringCharAt.invoke("foo", 0); if (value != 'f') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } MethodHandle bound = stringCharAt.bindTo("foo"); value = (char) bound.invoke(0); if (value != 'f') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } try { @@ -706,7 +707,7 @@ public class Main { bound = integerParseInt.bindTo("78452"); int intValue = (int) bound.invoke(); if (intValue != 78452) { - System.out.println("Unexpected value: " + intValue); + fail("Unexpected value: " + intValue); } } @@ -745,11 +746,11 @@ public class Main { boolean value = (boolean) adapter.invoke((int) 42); if (!value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } value = (boolean) adapter.invoke((int) 43); if (value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -764,7 +765,7 @@ public class Main { int value = (int) adapter.invoke("56"); if (value != 57) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -779,7 +780,7 @@ public class Main { int value = (int) adapter.invoke(); if (value != 42) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } } @@ -791,7 +792,7 @@ public class Main { return; } - System.out.println("Unexpected arguments: " + a + ", " + b + ", " + c + fail("Unexpected arguments: " + a + ", " + b + ", " + c + ", " + d + ", " + e + ", " + f + ", " + g + ", " + h); } @@ -800,7 +801,7 @@ public class Main { return; } - System.out.println("Unexpected arguments: " + a + ", " + b); + fail("Unexpected arguments: " + a + ", " + b); } public static void testPermuteArguments() throws Throwable { @@ -888,11 +889,48 @@ public class Main { } } + private static Object returnBar() { + return "bar"; + } + + public static void testInvokers() throws Throwable { + final MethodType targetType = MethodType.methodType(String.class, String.class); + final MethodHandle target = MethodHandles.lookup().findVirtual( + String.class, "concat", targetType); + + MethodHandle invoker = MethodHandles.invoker(target.type()); + assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar")); + assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar")); + try { + String foo = (String) invoker.invoke(target, "bar", "bar", 24); + fail(); + } catch (WrongMethodTypeException expected) { + } + + MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type()); + assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar")); + try { + String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar"); + fail(); + } catch (WrongMethodTypeException expected) { + } + try { + String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + public static void fail() { System.out.println("FAIL"); Thread.dumpStack(); } + public static void fail(String message) { + System.out.println("fail: " + message); + Thread.dumpStack(); + } + public static void assertEquals(String s1, String s2) { if (s1 == s2) { return; diff --git a/test/958-methodhandle-emulated-stackframe/run b/test/958-methodhandle-emulated-stackframe/run deleted file mode 100755 index a9f182288c..0000000000 --- a/test/958-methodhandle-emulated-stackframe/run +++ /dev/null @@ -1,20 +0,0 @@ -#!/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. - -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles diff --git a/test/959-invoke-polymorphic-accessors/run b/test/959-invoke-polymorphic-accessors/run deleted file mode 100644 index a9f182288c..0000000000 --- a/test/959-invoke-polymorphic-accessors/run +++ /dev/null @@ -1,20 +0,0 @@ -#!/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. - -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles diff --git a/test/Android.bp b/test/Android.bp index b0f0e5a98d..be5bc59e5c 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -265,6 +265,11 @@ art_cc_defaults { "922-properties/properties.cc", "923-monitors/monitors.cc", "924-threads/threads.cc", + "925-threadgroups/threadgroups.cc", + "927-timers/timers.cc", + "928-jni-table/jni_table.cc", + "929-search/search.cc", + "931-agent-thread/agent_thread.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index dd7876f09d..639996ee7f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -228,9 +228,15 @@ endef # name-to-var # Disable 153-reference-stress temporarily until a fix arrives. b/33389022. # Disable 080-oom-fragmentation due to flakes. b/33795328 +# Disable 497-inlining-and-class-loader and 542-unresolved-access-check until +# they are rewritten. These tests use a broken class loader that tries to +# register a dex file that's already registered with a different loader. +# b/34193123 ART_TEST_RUN_TEST_SKIP += \ 153-reference-stress \ - 080-oom-fragmentation + 080-oom-fragmentation \ + 497-inlining-and-class-loader \ + 542-unresolved-access-check ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ @@ -274,6 +280,7 @@ TEST_ART_BROKEN_TARGET_TESTS := \ # These 9** tests are not supported in current form due to linker # restrictions. See b/31681198 TEST_ART_BROKEN_TARGET_TESTS += \ + 901-hello-ti-agent \ 902-hello-transformation \ 903-hello-tagging \ 904-object-allocation \ @@ -297,6 +304,14 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 922-properties \ 923-monitors \ 924-threads \ + 925-threadgroups \ + 926-multi-obsolescence \ + 927-timers \ + 928-jni-table \ + 929-search \ + 930-hello-retransform \ + 931-agent-thread \ + 932-transform-saves \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -457,10 +472,12 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ 629-vdex-speed # This test fails without an image. -# 964 often times out due to the large number of classes it tries to compile. +# 018, 961, 964 often time out. b/34369284 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \ 137-cfi \ 138-duplicate-classes-check \ + 018-stack-overflow \ + 961-default-iface-resolution-gen \ 964-default-iface-init ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES))) @@ -565,6 +582,7 @@ TEST_ART_BROKEN_JIT_RUN_TESTS := \ 915-obsolete-2 \ 917-fields-transformation \ 919-obsolete-fields \ + 926-multi-obsolescence \ ifneq (,$(filter jit,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/XandY/Y.java b/test/XandY/Y.java index ecead6e35f..2a1f03698c 100644 --- a/test/XandY/Y.java +++ b/test/XandY/Y.java @@ -14,4 +14,8 @@ * limitations under the License. */ -class Y extends X {} +class Y extends X { + static Z z = new Z(); + static class Z { + } +} diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 1b6fc7033a..a841f9e6a2 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -33,12 +33,18 @@ namespace art { // public static native boolean hasJit(); -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) { +static jit::Jit* GetJitIfEnabled() { Runtime* runtime = Runtime::Current(); - return runtime != nullptr + bool can_jit = + runtime != nullptr && runtime->GetJit() != nullptr && runtime->GetInstrumentation()->GetCurrentInstrumentationLevel() != instrumentation::Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter; + return can_jit ? runtime->GetJit() : nullptr; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) { + return GetJitIfEnabled() != nullptr; } // public static native boolean hasOatFile(); @@ -152,7 +158,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, jstring method_name) { - jit::Jit* jit = Runtime::Current()->GetJit(); + jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return; } @@ -166,6 +172,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, CHECK(chars.c_str() != nullptr); method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName( chars.c_str(), kRuntimePointerSize); + if (method == nullptr) { + method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName( + chars.c_str(), kRuntimePointerSize); + } + DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str(); } jit::JitCodeCache* code_cache = jit->GetCodeCache(); diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 8245947251..28fa130443 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -44,7 +44,7 @@ STRIP_DEX="n" SECONDARY_DEX="" TIME_OUT="gdb" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb) # Value in seconds -if [ "$ART_USE_READ_BARRIER" = "true" ]; then +if [ "$ART_USE_READ_BARRIER" != "false" ]; then TIME_OUT_VALUE=2400 # 40 minutes. else TIME_OUT_VALUE=1200 # 20 minutes. @@ -62,6 +62,7 @@ DRY_RUN="n" # if y prepare to run the test but don't run it. TEST_VDEX="n" TEST_IS_NDEBUG="n" APP_IMAGE="y" +VDEX_FILTER="" while true; do if [ "x$1" = "x--quiet" ]; then @@ -256,6 +257,11 @@ while true; do elif [ "x$1" = "x--vdex" ]; then TEST_VDEX="y" shift + elif [ "x$1" = "x--vdex-filter" ]; then + shift + option="$1" + VDEX_FILTER="--compiler-filter=$option" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -322,6 +328,28 @@ if [ "$DEBUGGER" = "y" ]; then DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y" fi +if [ "$IS_JVMTI_TEST" = "y" ]; then + plugin=libopenjdkjvmtid.so + agent=libtiagentd.so + lib=tiagentd + if [[ "$TEST_IS_NDEBUG" = "y" ]]; then + agent=libtiagent.so + plugin=libopenjdkjvmti.so + lib=tiagent + fi + + ARGS="${ARGS} ${lib}" + if [[ "$USE_JVM" = "y" ]]; then + FLAGS="${FLAGS} -agentpath:${ANDROID_HOST_OUT}/nativetest64/${agent}=${TEST_NAME},jvm" + else + FLAGS="${FLAGS} -agentpath:${agent}=${TEST_NAME},art" + FLAGS="${FLAGS} -Xplugin:${plugin}" + FLAGS="${FLAGS} -Xfully-deoptable" + # Always make the compilation be debuggable. + COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable" + fi +fi + if [ "$USE_JVM" = "y" ]; then export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64 # Xmx is necessary since we don't pass down the ART flags to JVM. @@ -387,28 +415,6 @@ if [ "$JIT" = "y" ]; then fi fi -if [ "$IS_JVMTI_TEST" = "y" ]; then - plugin=libopenjdkjvmtid.so - agent=libtiagentd.so - lib=tiagentd - if [[ "$TEST_IS_NDEBUG" = "y" ]]; then - agent=libtiagent.so - plugin=libopenjdkjvmti.so - lib=tiagent - fi - - ARGS="${ARGS} ${lib}" - if [[ "$USE_JVM" = "y" ]]; then - FLAGS="${FLAGS} -agentpath:${agent}=${TEST_NAME},jvm" - else - FLAGS="${FLAGS} -agentpath:${agent}=${TEST_NAME},art" - FLAGS="${FLAGS} -Xplugin:${plugin}" - FLAGS="${FLAGS} -Xfully-deoptable" - # Always make the compilation be debuggable. - COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable" - fi -fi - JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni" if [ "$RELOCATE" = "y" ]; then @@ -514,7 +520,7 @@ if [ "$PREBUILD" = "y" ]; then dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}" fi if [ "$TEST_VDEX" = "y" ]; then - vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" + vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" fi fi diff --git a/test/run-test b/test/run-test index ea9622aa69..9b178021ff 100755 --- a/test/run-test +++ b/test/run-test @@ -354,6 +354,11 @@ while true; do elif [ "x$1" = "x--vdex" ]; then run_args="${run_args} --vdex" shift + elif [ "x$1" = "x--vdex-filter" ]; then + shift + filter=$1 + run_args="${run_args} --vdex-filter $filter" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 usage="yes" @@ -489,7 +494,7 @@ elif [ "$runtime" = "art" ]; then fi elif [ "$runtime" = "jvm" ]; then # TODO: Detect whether the host is 32-bit or 64-bit. - run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib64" + run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib64:${ANDROID_HOST_OUT}/nativetest64" fi if [ "$have_image" = "no" ]; then @@ -717,7 +722,7 @@ if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then # # TODO: Enable Checker when read barrier support is added to more # architectures (b/12687968). - if [ "x$ART_USE_READ_BARRIER" = xtrue ] \ + if [ "x$ART_USE_READ_BARRIER" != xfalse ] \ && (([ "x$host_mode" = "xyes" ] \ && ! arch_supports_read_barrier "$host_arch_name") \ || ([ "x$target_mode" = "xyes" ] \ diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc index 6f98f10072..4bceef53bb 100644 --- a/test/ti-agent/common_helper.cc +++ b/test/ti-agent/common_helper.cc @@ -18,6 +18,7 @@ #include <stdio.h> #include <sstream> +#include <deque> #include "art_method.h" #include "jni.h" @@ -60,50 +61,76 @@ bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) { return true; } -namespace common_redefine { -static void throwRedefinitionError(jvmtiEnv* jvmti, JNIEnv* env, jclass target, jvmtiError res) { +template <bool is_redefine> +static void throwCommonRedefinitionError(jvmtiEnv* jvmti, + JNIEnv* env, + jint num_targets, + jclass* target, + jvmtiError res) { std::stringstream err; - char* signature = nullptr; - char* generic = nullptr; - jvmti->GetClassSignature(target, &signature, &generic); char* error = nullptr; jvmti->GetErrorName(res, &error); - err << "Failed to redefine class <" << signature << "> due to " << error; + err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class"; + if (num_targets > 1) { + err << "es"; + } + err << " <"; + for (jint i = 0; i < num_targets; i++) { + char* signature = nullptr; + char* generic = nullptr; + jvmti->GetClassSignature(target[i], &signature, &generic); + if (i != 0) { + err << ", "; + } + err << signature; + jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature)); + jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic)); + } + err << "> due to " << error; std::string message = err.str(); - jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature)); - jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic)); jvmti->Deallocate(reinterpret_cast<unsigned char*>(error)); env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str()); } -using RedefineDirectFunction = jvmtiError (*)(jvmtiEnv*, jclass, jint, const unsigned char*); -static void DoClassTransformation(jvmtiEnv* jvmti_env, - JNIEnv* env, - jclass target, - jbyteArray class_file_bytes, - jbyteArray dex_file_bytes) { - jbyteArray desired_array = IsJVM() ? class_file_bytes : dex_file_bytes; - jint len = static_cast<jint>(env->GetArrayLength(desired_array)); - const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>( - env->GetByteArrayElements(desired_array, nullptr)); - jvmtiError res; - if (IsJVM()) { - jvmtiClassDefinition def; - def.klass = target; - def.class_byte_count = static_cast<jint>(len); - def.class_bytes = redef_bytes; - res = jvmti_env->RedefineClasses(1, &def); - } else { - RedefineDirectFunction f = - reinterpret_cast<RedefineDirectFunction>(jvmti_env->functions->reserved3); - res = f(jvmti_env, target, len, redef_bytes); +namespace common_redefine { + +static void throwRedefinitionError(jvmtiEnv* jvmti, + JNIEnv* env, + jint num_targets, + jclass* target, + jvmtiError res) { + return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res); +} + +static void DoMultiClassRedefine(jvmtiEnv* jvmti_env, + JNIEnv* env, + jint num_redefines, + jclass* targets, + jbyteArray* class_file_bytes, + jbyteArray* dex_file_bytes) { + std::vector<jvmtiClassDefinition> defs; + for (jint i = 0; i < num_redefines; i++) { + jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i]; + jint len = static_cast<jint>(env->GetArrayLength(desired_array)); + const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>( + env->GetByteArrayElements(desired_array, nullptr)); + defs.push_back({targets[i], static_cast<jint>(len), redef_bytes}); } + jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data()); if (res != JVMTI_ERROR_NONE) { - throwRedefinitionError(jvmti_env, env, target, res); + throwRedefinitionError(jvmti_env, env, num_redefines, targets, res); } } +static void DoClassRedefine(jvmtiEnv* jvmti_env, + JNIEnv* env, + jclass target, + jbyteArray class_file_bytes, + jbyteArray dex_file_bytes) { + return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes); +} + // Magic JNI export that classes can use for redefining classes. // To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRedefinition(JNIEnv* env, @@ -111,10 +138,41 @@ extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRedefinition(JNIEnv* en jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) { - DoClassTransformation(jvmti_env, env, target, class_file_bytes, dex_file_bytes); + DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes); } -// Don't do anything +// Magic JNI export that classes can use for redefining classes. +// To use classes should declare this as a native function with signature +// ([Ljava/lang/Class;[[B[[B)V +extern "C" JNIEXPORT void JNICALL Java_Main_doCommonMultiClassRedefinition( + JNIEnv* env, + jclass, + jobjectArray targets, + jobjectArray class_file_bytes, + jobjectArray dex_file_bytes) { + std::vector<jclass> classes; + std::vector<jbyteArray> class_files; + std::vector<jbyteArray> dex_files; + jint len = env->GetArrayLength(targets); + if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) { + env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), + "the three array arguments passed to this function have different lengths!"); + return; + } + for (jint i = 0; i < len; i++) { + classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i))); + dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i))); + class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i))); + } + return DoMultiClassRedefine(jvmti_env, + env, + len, + classes.data(), + class_files.data(), + dex_files.data()); +} + +// Get all capabilities except those related to retransformation. jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { @@ -122,10 +180,149 @@ jint OnLoad(JavaVM* vm, printf("Unable to get jvmti env!\n"); return 1; } - SetAllCapabilities(jvmti_env); + jvmtiCapabilities caps; + jvmti_env->GetPotentialCapabilities(&caps); + caps.can_retransform_classes = 0; + caps.can_retransform_any_class = 0; + jvmti_env->AddCapabilities(&caps); return 0; } } // namespace common_redefine +namespace common_retransform { + +struct CommonTransformationResult { + std::vector<unsigned char> class_bytes; + std::vector<unsigned char> dex_bytes; + + CommonTransformationResult(size_t class_size, size_t dex_size) + : class_bytes(class_size), dex_bytes(dex_size) {} + + CommonTransformationResult() = default; + CommonTransformationResult(CommonTransformationResult&&) = default; + CommonTransformationResult(CommonTransformationResult&) = default; +}; + +// Map from class name to transformation result. +std::map<std::string, std::deque<CommonTransformationResult>> gTransformations; + +extern "C" JNIEXPORT void JNICALL Java_Main_addCommonTransformationResult(JNIEnv* env, + jclass, + jstring class_name, + jbyteArray class_array, + jbyteArray dex_array) { + const char* name_chrs = env->GetStringUTFChars(class_name, nullptr); + std::string name_str(name_chrs); + env->ReleaseStringUTFChars(class_name, name_chrs); + CommonTransformationResult trans(env->GetArrayLength(class_array), + env->GetArrayLength(dex_array)); + if (env->ExceptionOccurred()) { + return; + } + env->GetByteArrayRegion(class_array, + 0, + env->GetArrayLength(class_array), + reinterpret_cast<jbyte*>(trans.class_bytes.data())); + if (env->ExceptionOccurred()) { + return; + } + env->GetByteArrayRegion(dex_array, + 0, + env->GetArrayLength(dex_array), + reinterpret_cast<jbyte*>(trans.dex_bytes.data())); + if (env->ExceptionOccurred()) { + return; + } + if (gTransformations.find(name_str) == gTransformations.end()) { + std::deque<CommonTransformationResult> list; + gTransformations[name_str] = std::move(list); + } + gTransformations[name_str].push_back(std::move(trans)); +} + +// The hook we are using. +void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env, + JNIEnv* jni_env ATTRIBUTE_UNUSED, + jclass class_being_redefined ATTRIBUTE_UNUSED, + jobject loader ATTRIBUTE_UNUSED, + const char* name, + jobject protection_domain ATTRIBUTE_UNUSED, + jint class_data_len ATTRIBUTE_UNUSED, + const unsigned char* class_dat ATTRIBUTE_UNUSED, + jint* new_class_data_len, + unsigned char** new_class_data) { + std::string name_str(name); + if (gTransformations.find(name_str) != gTransformations.end() && + gTransformations[name_str].size() > 0) { + CommonTransformationResult& res = gTransformations[name_str][0]; + const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes; + unsigned char* new_data; + CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data)); + memcpy(new_data, desired_array.data(), desired_array.size()); + *new_class_data = new_data; + *new_class_data_len = desired_array.size(); + gTransformations[name_str].pop_front(); + } +} + +extern "C" JNIEXPORT void Java_Main_enableCommonRetransformation(JNIEnv* env, + jclass, + jboolean enable) { + jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + nullptr); + if (res != JVMTI_ERROR_NONE) { + JvmtiErrorToException(env, res); + } +} + +static void throwRetransformationError(jvmtiEnv* jvmti, + JNIEnv* env, + jint num_targets, + jclass* targets, + jvmtiError res) { + return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res); +} + +static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) { + std::vector<jclass> classes; + jint len = env->GetArrayLength(targets); + for (jint i = 0; i < len; i++) { + classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i))); + } + jvmtiError res = jvmti_env->RetransformClasses(len, classes.data()); + if (res != JVMTI_ERROR_NONE) { + throwRetransformationError(jvmti_env, env, len, classes.data(), res); + } +} + +// TODO Write something useful. +extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRetransformation(JNIEnv* env, + jclass, + jobjectArray targets) { + DoClassRetransformation(jvmti_env, env, targets); +} + +// 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; + } + SetAllCapabilities(jvmti_env); + 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_retransform + } // namespace art diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h index 642ca03274..8599fc4d6c 100644 --- a/test/ti-agent/common_helper.h +++ b/test/ti-agent/common_helper.h @@ -27,6 +27,10 @@ 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 + extern bool RuntimeIsJVM; diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 8abd063a01..f4ce4c381e 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -64,7 +64,10 @@ AgentLib agents[] = { { "916-obsolete-jit", common_redefine::OnLoad, nullptr }, { "917-fields-transformation", common_redefine::OnLoad, nullptr }, { "919-obsolete-fields", common_redefine::OnLoad, nullptr }, - { "921-hello-failure", common_redefine::OnLoad, nullptr }, + { "921-hello-failure", common_retransform::OnLoad, nullptr }, + { "926-multi-obsolescence", common_redefine::OnLoad, nullptr }, + { "930-hello-retransform", common_retransform::OnLoad, nullptr }, + { "932-transform-saves", common_retransform::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt index fd3c3318ce..c775f98b70 100644 --- a/test/valgrind-suppressions.txt +++ b/test/valgrind-suppressions.txt @@ -22,3 +22,50 @@ ... fun:_ZN3art7Runtime17InitNativeMethodsEv } + +# SigQuit runs libbacktrace +{ + BackTraceReading64 + Memcheck:Addr8 + fun:access_mem_unrestricted + fun:_Uelf64_memory_read + fun:_Uelf64_valid_object_memory + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading32 + Memcheck:Addr4 + fun:access_mem_unrestricted + fun:_Uelf32_memory_read + fun:_Uelf32_valid_object_memory + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading64 + Memcheck:Addr8 + fun:access_mem_unrestricted + fun:_Uelf64_memory_read + fun:_Uelf64_get_load_base + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} +{ + BackTraceReading32 + Memcheck:Addr4 + fun:access_mem_unrestricted + fun:_Uelf32_memory_read + fun:_Uelf32_get_load_base + fun:map_create_list + fun:unw_map_local_create + fun:_ZN14UnwindMapLocal5BuildEv + fun:_ZN12BacktraceMap6CreateEib +} + diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def index abb5e1ecac..43f94344cc 100644 --- a/tools/cpp-define-generator/offset_dexcache.def +++ b/tools/cpp-define-generator/offset_dexcache.def @@ -34,7 +34,6 @@ // New macro suffix Method Name (of the Offset method) DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_METHODS, DexCacheResolvedMethods) -DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_TYPES, DexCacheResolvedTypes) DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni) DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode) DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass) diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java index 99e03e8ea7..af8a05cdbc 100644 --- a/tools/dexfuzz/src/dexfuzz/Options.java +++ b/tools/dexfuzz/src/dexfuzz/Options.java @@ -50,6 +50,7 @@ public class Options { public static String deviceName = ""; public static boolean usingSpecificDevice = false; public static int repeat = 1; + public static int divergenceRetry = 10; public static String executeDirectory = "/data/art-test"; public static String androidRoot = ""; public static String dumpMutationsFile = "mutations.dump"; @@ -118,6 +119,8 @@ public class Options { Log.always(" --repeat=<n> : Fuzz N programs, executing each one."); Log.always(" --short-timeouts : Shorten timeouts (faster; use if"); Log.always(" you want to focus on output divergences)"); + Log.always(" --divergence-retry=<n> : Number of retries when checking if test is"); + Log.always(" self-divergent. (Default: 10)"); Log.always(" --seed=<seed> : RNG seed to use"); Log.always(" --method-mutations=<n> : Maximum number of mutations to perform on each method."); Log.always(" (Default: 3)"); @@ -239,6 +242,8 @@ public class Options { maxMethods = Integer.parseInt(value); } else if (key.equals("repeat")) { repeat = Integer.parseInt(value); + } else if (key.equals("divergence-retry")) { + divergenceRetry = Integer.parseInt(value); } else if (key.equals("log")) { Log.setLoggingLevel(LogTag.valueOf(value.toUpperCase())); } else if (key.equals("likelihoods")) { @@ -360,6 +365,10 @@ public class Options { Log.error("--repeat must be at least 1!"); return false; } + if (divergenceRetry < 0) { + Log.error("--divergence-retry cannot be negative!"); + return false; + } if (usingProvidedSeed && repeat > 1) { Log.error("Cannot use --repeat with --seed"); return false; diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java index 1797d90141..ccc426c5e4 100644 --- a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java +++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java @@ -298,13 +298,13 @@ public abstract class Fuzzer { } private boolean checkGoldenExecutorForSelfDivergence(String programName) { - // Run golden executor 5 times, make sure it always produces + // Run golden executor multiple times, make sure it always produces // the same output, otherwise report that it is self-divergent. // TODO: Instead, produce a list of acceptable outputs, and see if the divergent // outputs of the backends fall within this set of outputs. String seenOutput = null; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < Options.divergenceRetry + 1; i++) { goldenExecutor.reset(); goldenExecutor.execute(programName); String output = goldenExecutor.getResult().getFlattenedOutput(); |