diff options
90 files changed, 1807 insertions, 1055 deletions
diff --git a/build/Android.oat.mk b/build/Android.oat.mk index c4887e61ff..e297b4f531 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -122,7 +122,8 @@ $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \ --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \ $$(LOCAL_$(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \ - --host --android-root=$$(HOST_OUT) --include-patch-information --generate-debug-info \ + --host --android-root=$$(HOST_OUT) --include-patch-information \ + --generate-debug-info --generate-build-id \ $$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS) $$(core_oat_name): $$(core_image_name) @@ -239,7 +240,8 @@ $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency) --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \ --instruction-set-variant=$$($(3)DEX2OAT_TARGET_CPU_VARIANT) \ --instruction-set-features=$$($(3)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \ - --android-root=$$(PRODUCT_OUT)/system --include-patch-information --generate-debug-info \ + --android-root=$$(PRODUCT_OUT)/system --include-patch-information \ + --generate-debug-info --generate-build-id \ $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1) $$(core_oat_name): $$(core_image_name) diff --git a/build/art.go b/build/art.go index b826538696..1164cbc553 100644 --- a/build/art.go +++ b/build/art.go @@ -15,7 +15,6 @@ package art import ( - "android/soong" "android/soong/android" "android/soong/cc" "fmt" @@ -222,13 +221,13 @@ func testInstall(ctx android.InstallHookContext) { var artTestMutex sync.Mutex func init() { - soong.RegisterModuleType("art_cc_library", artLibrary) - soong.RegisterModuleType("art_cc_binary", artBinary) - soong.RegisterModuleType("art_cc_test", artTest) - soong.RegisterModuleType("art_cc_test_library", artTestLibrary) - soong.RegisterModuleType("art_cc_defaults", artDefaultsFactory) - soong.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory) - soong.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory) + android.RegisterModuleType("art_cc_library", artLibrary) + android.RegisterModuleType("art_cc_binary", artBinary) + android.RegisterModuleType("art_cc_test", artTest) + android.RegisterModuleType("art_cc_test_library", artTestLibrary) + android.RegisterModuleType("art_cc_defaults", artDefaultsFactory) + android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory) + android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory) } func artGlobalDefaultsFactory() (blueprint.Module, []interface{}) { diff --git a/compiler/Android.bp b/compiler/Android.bp index 6faac095c7..61f682c2bd 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -191,6 +191,14 @@ art_cc_defaults { ], include_dirs: ["art/disassembler"], export_include_dirs: ["."], + + // For SHA-1 checksumming of build ID + static: { + whole_static_libs: ["libcrypto"], + }, + shared: { + shared_libs: ["libcrypto"], + }, } gensrcs { diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 0884a2a9f5..d807fcad96 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -31,10 +31,6 @@ namespace art { -inline mirror::DexCache* CompilerDriver::GetDexCache(const DexCompilationUnit* mUnit) { - return mUnit->GetClassLinker()->FindDexCache(Thread::Current(), *mUnit->GetDexFile(), false); -} - inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa, const DexCompilationUnit* mUnit) { return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr(); @@ -87,10 +83,6 @@ inline ArtField* CompilerDriver::ResolveFieldWithDexFile( return resolved_field; } -inline mirror::DexCache* CompilerDriver::FindDexCache(const DexFile* dex_file) { - return Runtime::Current()->GetClassLinker()->FindDexCache(Thread::Current(), *dex_file, false); -} - inline ArtField* CompilerDriver::ResolveField( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, @@ -100,23 +92,6 @@ inline ArtField* CompilerDriver::ResolveField( is_static); } -inline void CompilerDriver::GetResolvedFieldDexFileLocation( - ArtField* resolved_field, const DexFile** declaring_dex_file, - uint16_t* declaring_class_idx, uint16_t* declaring_field_idx) { - ObjPtr<mirror::Class> declaring_class = resolved_field->GetDeclaringClass(); - *declaring_dex_file = declaring_class->GetDexCache()->GetDexFile(); - *declaring_class_idx = declaring_class->GetDexTypeIndex(); - *declaring_field_idx = resolved_field->GetDexFieldIndex(); -} - -inline bool CompilerDriver::IsFieldVolatile(ArtField* field) { - return field->IsVolatile(); -} - -inline MemberOffset CompilerDriver::GetFieldOffset(ArtField* field) { - return field->GetOffset(); -} - inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField( mirror::DexCache* dex_cache, mirror::Class* referrer_class, ArtField* resolved_field, uint16_t field_idx) { @@ -219,43 +194,6 @@ inline bool CompilerDriver::IsClassOfStaticMethodAvailableToReferrer( return result.first; } -inline bool CompilerDriver::IsStaticFieldInReferrerClass(mirror::Class* referrer_class, - ArtField* resolved_field) { - DCHECK(resolved_field->IsStatic()); - ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass(); - return referrer_class == fields_class; -} - -inline bool CompilerDriver::CanAssumeClassIsInitialized(mirror::Class* klass) { - // Being loaded is a pre-requisite for being initialized but let's do the cheap check first. - // - // NOTE: When AOT compiling an app, we eagerly initialize app classes (and potentially their - // super classes in the boot image) but only those that have a trivial initialization, i.e. - // without <clinit>() or static values in the dex file for that class or any of its super - // classes. So while we could see the klass as initialized during AOT compilation and have - // it only loaded at runtime, the needed initialization would have to be trivial and - // unobservable from Java, so we may as well treat it as initialized. - if (!klass->IsInitialized()) { - return false; - } - return CanAssumeClassIsLoaded(klass); -} - -inline bool CompilerDriver::CanReferrerAssumeClassIsInitialized(mirror::Class* referrer_class, - mirror::Class* klass) { - return (referrer_class != nullptr - && !referrer_class->IsInterface() - && referrer_class->IsSubClass(klass)) - || CanAssumeClassIsInitialized(klass); -} - -inline bool CompilerDriver::IsStaticFieldsClassInitialized(mirror::Class* referrer_class, - ArtField* resolved_field) { - DCHECK(resolved_field->IsStatic()); - ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass(); - return CanReferrerAssumeClassIsInitialized(referrer_class, fields_class.Ptr()); -} - inline ArtMethod* CompilerDriver::ResolveMethod( ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, @@ -275,35 +213,6 @@ inline ArtMethod* CompilerDriver::ResolveMethod( return resolved_method; } -inline void CompilerDriver::GetResolvedMethodDexFileLocation( - ArtMethod* resolved_method, const DexFile** declaring_dex_file, - uint16_t* declaring_class_idx, uint16_t* declaring_method_idx) { - mirror::Class* declaring_class = resolved_method->GetDeclaringClass(); - *declaring_dex_file = declaring_class->GetDexCache()->GetDexFile(); - *declaring_class_idx = declaring_class->GetDexTypeIndex(); - *declaring_method_idx = resolved_method->GetDexMethodIndex(); -} - -inline uint16_t CompilerDriver::GetResolvedMethodVTableIndex( - ArtMethod* resolved_method, InvokeType type) { - if (type == kVirtual || type == kSuper) { - return resolved_method->GetMethodIndex(); - } else if (type == kInterface) { - return resolved_method->GetDexMethodIndex(); - } else { - return DexFile::kDexNoIndex16; - } -} - -inline bool CompilerDriver::IsMethodsClassInitialized(mirror::Class* referrer_class, - ArtMethod* resolved_method) { - if (!resolved_method->IsStatic()) { - return true; - } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - return CanReferrerAssumeClassIsInitialized(referrer_class, methods_class); -} - } // namespace art #endif // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e2f8d929c3..8d64c65b1d 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -95,8 +95,6 @@ class CompilerDriver::AOTCompilationStats { public: AOTCompilationStats() : stats_lock_("AOT compilation statistics lock"), - types_in_dex_cache_(0), types_not_in_dex_cache_(0), - strings_in_dex_cache_(0), strings_not_in_dex_cache_(0), resolved_types_(0), unresolved_types_(0), resolved_instance_fields_(0), unresolved_instance_fields_(0), resolved_local_static_fields_(0), resolved_static_fields_(0), unresolved_static_fields_(0), @@ -112,8 +110,6 @@ class CompilerDriver::AOTCompilationStats { } void Dump() { - DumpStat(types_in_dex_cache_, types_not_in_dex_cache_, "types known to be in dex cache"); - DumpStat(strings_in_dex_cache_, strings_not_in_dex_cache_, "strings known to be in dex cache"); DumpStat(resolved_types_, unresolved_types_, "types resolved"); DumpStat(resolved_instance_fields_, unresolved_instance_fields_, "instance fields resolved"); DumpStat(resolved_local_static_fields_ + resolved_static_fields_, unresolved_static_fields_, @@ -164,26 +160,6 @@ class CompilerDriver::AOTCompilationStats { #define STATS_LOCK() #endif - void TypeInDexCache() REQUIRES(!stats_lock_) { - STATS_LOCK(); - types_in_dex_cache_++; - } - - void TypeNotInDexCache() REQUIRES(!stats_lock_) { - STATS_LOCK(); - types_not_in_dex_cache_++; - } - - void StringInDexCache() REQUIRES(!stats_lock_) { - STATS_LOCK(); - strings_in_dex_cache_++; - } - - void StringNotInDexCache() REQUIRES(!stats_lock_) { - STATS_LOCK(); - strings_not_in_dex_cache_++; - } - void TypeDoesntNeedAccessCheck() REQUIRES(!stats_lock_) { STATS_LOCK(); resolved_types_++; @@ -225,67 +201,6 @@ class CompilerDriver::AOTCompilationStats { type_based_devirtualization_++; } - // Indicate that a method of the given type was resolved at compile time. - void ResolvedMethod(InvokeType type) REQUIRES(!stats_lock_) { - DCHECK_LE(type, kMaxInvokeType); - STATS_LOCK(); - resolved_methods_[type]++; - } - - // Indicate that a method of the given type was unresolved at compile time as it was in an - // unknown dex file. - void UnresolvedMethod(InvokeType type) REQUIRES(!stats_lock_) { - DCHECK_LE(type, kMaxInvokeType); - STATS_LOCK(); - unresolved_methods_[type]++; - } - - // Indicate that a type of virtual method dispatch has been converted into a direct method - // dispatch. - void VirtualMadeDirect(InvokeType type) REQUIRES(!stats_lock_) { - DCHECK(type == kVirtual || type == kInterface || type == kSuper); - STATS_LOCK(); - virtual_made_direct_[type]++; - } - - // Indicate that a method of the given type was able to call directly into boot. - void DirectCallsToBoot(InvokeType type) REQUIRES(!stats_lock_) { - DCHECK_LE(type, kMaxInvokeType); - STATS_LOCK(); - direct_calls_to_boot_[type]++; - } - - // Indicate that a method of the given type was able to be resolved directly from boot. - void DirectMethodsToBoot(InvokeType type) REQUIRES(!stats_lock_) { - DCHECK_LE(type, kMaxInvokeType); - STATS_LOCK(); - direct_methods_to_boot_[type]++; - } - - void ProcessedInvoke(InvokeType type, int flags) REQUIRES(!stats_lock_) { - STATS_LOCK(); - if (flags == 0) { - unresolved_methods_[type]++; - } else { - DCHECK_NE((flags & kFlagMethodResolved), 0); - resolved_methods_[type]++; - if ((flags & kFlagVirtualMadeDirect) != 0) { - virtual_made_direct_[type]++; - if ((flags & kFlagPreciseTypeDevirtualization) != 0) { - type_based_devirtualization_++; - } - } else { - DCHECK_EQ((flags & kFlagPreciseTypeDevirtualization), 0); - } - if ((flags & kFlagDirectCallToBoot) != 0) { - direct_calls_to_boot_[type]++; - } - if ((flags & kFlagDirectMethodToBoot) != 0) { - direct_methods_to_boot_[type]++; - } - } - } - // A check-cast could be eliminated due to verifier type analysis. void SafeCast() REQUIRES(!stats_lock_) { STATS_LOCK(); @@ -301,12 +216,6 @@ class CompilerDriver::AOTCompilationStats { private: Mutex stats_lock_; - size_t types_in_dex_cache_; - size_t types_not_in_dex_cache_; - - size_t strings_in_dex_cache_; - size_t strings_not_in_dex_cache_; - size_t resolved_types_; size_t unresolved_types_; @@ -472,7 +381,8 @@ static void SetupIntrinsic(Thread* self, ? cls->FindDeclaredDirectMethod(method_name, signature, image_size) : cls->FindDeclaredVirtualMethod(method_name, signature, image_size); if (method == nullptr) { - LOG(FATAL) << "Could not find method of intrinsic " << class_name << method_name << signature; + LOG(FATAL) << "Could not find method of intrinsic " + << class_name << " " << method_name << " " << signature; } DCHECK_EQ(method->GetInvokeType(), invoke_type); method->SetIntrinsic(static_cast<uint32_t>(intrinsic)); @@ -497,11 +407,13 @@ void CompilerDriver::CompileAll(jobject class_loader, // those compilations will pick up a boot image that have the ArtMethod already // set with the intrinsics flag. ScopedObjectAccess soa(Thread::Current()); -#define OPTIMIZING_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, ClassName, MethodName, Signature) \ +#define SETUP_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, \ + ClassName, MethodName, Signature) \ SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature); #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +INTRINSICS_LIST(SETUP_INTRINSICS) #undef INTRINSICS_LIST +#undef SETUP_INTRINSICS } // Compile: // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex @@ -845,9 +757,10 @@ void CompilerDriver::Resolve(jobject class_loader, // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a // stable order. -static void ResolveConstStrings(CompilerDriver* driver, +static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache, const DexFile& dex_file, - const DexFile::CodeItem* code_item) { + const DexFile::CodeItem* code_item) + REQUIRES_SHARED(Locks::mutator_lock_) { if (code_item == nullptr) { // Abstract or native method. return; @@ -855,18 +768,18 @@ static void ResolveConstStrings(CompilerDriver* driver, const uint16_t* code_ptr = code_item->insns_; const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_; + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); while (code_ptr < code_end) { const Instruction* inst = Instruction::At(code_ptr); switch (inst->Opcode()) { - case Instruction::CONST_STRING: { - uint32_t string_index = inst->VRegB_21c(); - driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index); - break; - } + case Instruction::CONST_STRING: case Instruction::CONST_STRING_JUMBO: { - uint32_t string_index = inst->VRegB_31c(); - driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index); + uint32_t string_index = (inst->Opcode() == Instruction::CONST_STRING) + ? inst->VRegB_21c() + : inst->VRegB_31c(); + mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); + CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; break; } @@ -881,7 +794,13 @@ static void ResolveConstStrings(CompilerDriver* driver, static void ResolveConstStrings(CompilerDriver* driver, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); + for (const DexFile* dex_file : dex_files) { + dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file, false)); TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); size_t class_def_count = dex_file->NumClassDefs(); @@ -922,7 +841,7 @@ static void ResolveConstStrings(CompilerDriver* driver, continue; } previous_direct_method_idx = method_idx; - ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem()); + ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } // Virtual methods. @@ -936,7 +855,7 @@ static void ResolveConstStrings(CompilerDriver* driver, continue; } previous_virtual_method_idx = method_idx; - ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem()); + ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -1407,54 +1326,6 @@ void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodRefere dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index); } -bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache, - uint32_t type_idx) { - bool result = false; - if ((GetCompilerOptions().IsBootImage() && - IsImageClass(dex_cache->GetDexFile()->StringDataByIdx( - dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) || - Runtime::Current()->UseJitCompilation()) { - mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); - result = (resolved_class != nullptr); - } - - if (result) { - stats_->TypeInDexCache(); - } else { - stats_->TypeNotInDexCache(); - } - return result; -} - -bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, - uint32_t string_idx) { - // See also Compiler::ResolveDexFile - - bool result = false; - if (GetCompilerOptions().IsBootImage() || Runtime::Current()->UseJitCompilation()) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache( - soa.Self(), dex_file, false))); - if (GetCompilerOptions().IsBootImage()) { - // We resolve all const-string strings when building for the image. - class_linker->ResolveString(dex_file, string_idx, dex_cache); - result = true; - } else { - // Just check whether the dex cache already has the string. - DCHECK(Runtime::Current()->UseJitCompilation()); - result = (dex_cache->GetResolvedString(string_idx) != nullptr); - } - } - if (result) { - stats_->StringInDexCache(); - } else { - stats_->StringNotInDexCache(); - } - return result; -} - bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, Handle<mirror::DexCache> dex_cache, uint32_t type_idx) { @@ -1518,108 +1389,6 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id return result; } -bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx, - bool* is_type_initialized, bool* use_direct_type_ptr, - uintptr_t* direct_type_ptr, bool* out_is_finalizable) { - ScopedObjectAccess soa(Thread::Current()); - Runtime* runtime = Runtime::Current(); - mirror::DexCache* dex_cache = runtime->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); - mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); - if (resolved_class == nullptr) { - return false; - } - if (GetCompilerOptions().GetCompilePic()) { - // Do not allow a direct class pointer to be used when compiling for position-independent - return false; - } - *out_is_finalizable = resolved_class->IsFinalizable(); - gc::Heap* heap = runtime->GetHeap(); - const bool compiling_boot = heap->IsCompilingBoot(); - const bool support_boot_image_fixup = GetSupportBootImageFixup(); - if (compiling_boot) { - // boot -> boot class pointers. - // True if the class is in the image at boot compiling time. - const bool is_image_class = GetCompilerOptions().IsBootImage() && IsImageClass( - dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_)); - // True if pc relative load works. - if (is_image_class && support_boot_image_fixup) { - *is_type_initialized = resolved_class->IsInitialized(); - *use_direct_type_ptr = false; - *direct_type_ptr = 0; - return true; - } else { - return false; - } - } else if (runtime->UseJitCompilation() && !heap->IsMovableObject(resolved_class)) { - *is_type_initialized = resolved_class->IsInitialized(); - // If the class may move around, then don't embed it as a direct pointer. - *use_direct_type_ptr = true; - *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class); - return true; - } else { - // True if the class is in the image at app compiling time. - const bool class_in_image = heap->FindSpaceFromObject(resolved_class, false)->IsImageSpace(); - if (class_in_image && support_boot_image_fixup) { - // boot -> app class pointers. - *is_type_initialized = resolved_class->IsInitialized(); - // TODO This is somewhat hacky. We should refactor all of this invoke codepath. - *use_direct_type_ptr = !GetCompilerOptions().GetIncludePatchInformation(); - *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class); - return true; - } else { - // app -> app class pointers. - // Give up because app does not have an image and class - // isn't created at compile time. TODO: implement this - // if/when each app gets an image. - return false; - } - } -} - -bool CompilerDriver::CanEmbedReferenceTypeInCode(ClassReference* ref, - bool* use_direct_ptr, - uintptr_t* direct_type_ptr) { - CHECK(ref != nullptr); - CHECK(use_direct_ptr != nullptr); - CHECK(direct_type_ptr != nullptr); - - ScopedObjectAccess soa(Thread::Current()); - mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference(); - bool is_initialized = false; - bool unused_finalizable; - // Make sure we have a finished Reference class object before attempting to use it. - if (!CanEmbedTypeInCode(*reference_class->GetDexCache()->GetDexFile(), - reference_class->GetDexTypeIndex(), &is_initialized, - use_direct_ptr, direct_type_ptr, &unused_finalizable) || - !is_initialized) { - return false; - } - ref->first = &reference_class->GetDexFile(); - ref->second = reference_class->GetDexClassDefIndex(); - return true; -} - -uint32_t CompilerDriver::GetReferenceSlowFlagOffset() const { - ScopedObjectAccess soa(Thread::Current()); - mirror::Class* klass = mirror::Reference::GetJavaLangRefReference(); - DCHECK(klass->IsInitialized()); - return klass->GetSlowPathFlagOffset().Uint32Value(); -} - -uint32_t CompilerDriver::GetReferenceDisableFlagOffset() const { - ScopedObjectAccess soa(Thread::Current()); - mirror::Class* klass = mirror::Reference::GetJavaLangRefReference(); - DCHECK(klass->IsInitialized()); - return klass->GetDisableIntrinsicFlagOffset().Uint32Value(); -} - -DexCacheArraysLayout CompilerDriver::GetDexCacheArraysLayout(const DexFile* dex_file) { - return ContainsElement(GetDexFilesForOatFile(), dex_file) - ? DexCacheArraysLayout(GetInstructionSetPointerSize(instruction_set_), dex_file) - : DexCacheArraysLayout(); -} - void CompilerDriver::ProcessedInstanceField(bool resolved) { if (!resolved) { stats_->UnresolvedInstanceField(); @@ -1638,10 +1407,6 @@ void CompilerDriver::ProcessedStaticField(bool resolved, bool local) { } } -void CompilerDriver::ProcessedInvoke(InvokeType invoke_type, int flags) { - stats_->ProcessedInvoke(invoke_type, flags); -} - ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, const ScopedObjectAccess& soa) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index eb1222c315..9a4dd857fc 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -182,15 +182,6 @@ class CompilerDriver { uint16_t class_def_index) REQUIRES(!requires_constructor_barrier_lock_); - // Callbacks from compiler to see what runtime checks must be generated. - - bool CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache, - uint32_t type_idx) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx) - REQUIRES(!Locks::mutator_lock_); - // Are runtime access checks necessary in the compiled code? bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, Handle<mirror::DexCache> dex_cache, @@ -205,24 +196,6 @@ class CompilerDriver { bool* out_is_finalizable) REQUIRES_SHARED(Locks::mutator_lock_); - bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx, - bool* is_type_initialized, bool* use_direct_type_ptr, - uintptr_t* direct_type_ptr, bool* out_is_finalizable); - - // Query methods for the java.lang.ref.Reference class. - bool CanEmbedReferenceTypeInCode(ClassReference* ref, - bool* use_direct_type_ptr, uintptr_t* direct_type_ptr); - uint32_t GetReferenceSlowFlagOffset() const; - uint32_t GetReferenceDisableFlagOffset() const; - - // Get the DexCache for the - mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit) - REQUIRES_SHARED(Locks::mutator_lock_); - - mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit) - REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve compiling method's class. Returns null on failure. mirror::Class* ResolveCompilingMethodsClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, @@ -250,19 +223,6 @@ class CompilerDriver { uint32_t field_idx, bool is_static) REQUIRES_SHARED(Locks::mutator_lock_); - // Get declaration location of a resolved field. - void GetResolvedFieldDexFileLocation( - ArtField* resolved_field, const DexFile** declaring_dex_file, - uint16_t* declaring_class_idx, uint16_t* declaring_field_idx) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool IsFieldVolatile(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); - MemberOffset GetFieldOffset(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); - - // Find a dex cache for a dex file. - inline mirror::DexCache* FindDexCache(const DexFile* dex_file) - REQUIRES_SHARED(Locks::mutator_lock_); - // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. std::pair<bool, bool> IsFastInstanceField( mirror::DexCache* dex_cache, mirror::Class* referrer_class, @@ -288,15 +248,6 @@ class CompilerDriver { uint32_t* storage_index) REQUIRES_SHARED(Locks::mutator_lock_); - // Is static field's in referrer's class? - bool IsStaticFieldInReferrerClass(mirror::Class* referrer_class, ArtField* resolved_field) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Is static field's class initialized? - bool IsStaticFieldsClassInitialized(mirror::Class* referrer_class, - ArtField* resolved_field) - REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a method. Returns null on failure, including incompatible class change. ArtMethod* ResolveMethod( ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, @@ -304,37 +255,8 @@ class CompilerDriver { uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true) REQUIRES_SHARED(Locks::mutator_lock_); - // Get declaration location of a resolved field. - void GetResolvedMethodDexFileLocation( - ArtMethod* resolved_method, const DexFile** declaring_dex_file, - uint16_t* declaring_class_idx, uint16_t* declaring_method_idx) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Get the index in the vtable of the method. - uint16_t GetResolvedMethodVTableIndex( - ArtMethod* resolved_method, InvokeType type) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Is method's class initialized for an invoke? - // For static invokes to determine whether we need to consider potential call to <clinit>(). - // For non-static invokes, assuming a non-null reference, the class is always initialized. - bool IsMethodsClassInitialized(mirror::Class* referrer_class, ArtMethod* resolved_method) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Get the layout of dex cache arrays for a dex file. Returns invalid layout if the - // dex cache arrays don't have a fixed layout. - DexCacheArraysLayout GetDexCacheArraysLayout(const DexFile* dex_file); - void ProcessedInstanceField(bool resolved); void ProcessedStaticField(bool resolved, bool local); - void ProcessedInvoke(InvokeType invoke_type, int flags); - - void ComputeFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - const ScopedObjectAccess& soa, bool is_static, - ArtField** resolved_field, - mirror::Class** referrer_class, - mirror::DexCache** dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); // Can we fast path instance field access? Computes field's offset and volatility. bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, @@ -386,6 +308,7 @@ class CompilerDriver { void SetDedupeEnabled(bool dedupe_enabled) { compiled_method_storage_.SetDedupeEnabled(dedupe_enabled); } + bool DedupeEnabled() const { return compiled_method_storage_.DedupeEnabled(); } @@ -449,6 +372,13 @@ class CompilerDriver { return current_dex_to_dex_methods_; } + // Compute constant code and method pointers when possible. + void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class, + ArtMethod* method, + /* out */ uintptr_t* direct_code, + /* out */ uintptr_t* direct_method) + REQUIRES_SHARED(Locks::mutator_lock_); + private: // Return whether the declaring class of `resolved_member` is // available to `referrer_class` for read or write access using two @@ -477,38 +407,9 @@ class CompilerDriver { uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); - // Can we assume that the klass is initialized? - bool CanAssumeClassIsInitialized(mirror::Class* klass) - REQUIRES_SHARED(Locks::mutator_lock_); - bool CanReferrerAssumeClassIsInitialized(mirror::Class* referrer_class, mirror::Class* klass) - REQUIRES_SHARED(Locks::mutator_lock_); - - // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics. - // The only external contract is that unresolved method has flags 0 and resolved non-0. - enum { - kBitMethodResolved = 0, - kBitVirtualMadeDirect, - kBitPreciseTypeDevirtualization, - kBitDirectCallToBoot, - kBitDirectMethodToBoot - }; - static constexpr int kFlagMethodResolved = 1 << kBitMethodResolved; - static constexpr int kFlagVirtualMadeDirect = 1 << kBitVirtualMadeDirect; - static constexpr int kFlagPreciseTypeDevirtualization = 1 << kBitPreciseTypeDevirtualization; - static constexpr int kFlagDirectCallToBoot = 1 << kBitDirectCallToBoot; - static constexpr int kFlagDirectMethodToBoot = 1 << kBitDirectMethodToBoot; - static constexpr int kFlagsMethodResolvedVirtualMadeDirect = - kFlagMethodResolved | kFlagVirtualMadeDirect; - static constexpr int kFlagsMethodResolvedPreciseTypeDevirtualization = - kFlagsMethodResolvedVirtualMadeDirect | kFlagPreciseTypeDevirtualization; - - public: // TODO make private or eliminate. - // Compute constant code and method pointers when possible. - void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class, - ArtMethod* method, - /* out */ uintptr_t* direct_code, - /* out */ uintptr_t* direct_method) - REQUIRES_SHARED(Locks::mutator_lock_); + mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa, + const DexCompilationUnit* mUnit) + REQUIRES_SHARED(Locks::mutator_lock_); private: void PreCompile(jobject class_loader, @@ -566,8 +467,6 @@ class CompilerDriver { REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); - static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) - REQUIRES_SHARED(Locks::mutator_lock_); void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files, diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index cbcc169f41..c222f90043 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -37,6 +37,7 @@ CompilerOptions::CompilerOptions() debuggable_(false), generate_debug_info_(kDefaultGenerateDebugInfo), generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), + generate_build_id_(false), implicit_null_checks_(true), implicit_so_checks_(true), implicit_suspend_checks_(false), @@ -97,6 +98,7 @@ CompilerOptions::CompilerOptions(CompilerFilter::Filter compiler_filter, debuggable_(debuggable), generate_debug_info_(generate_debug_info), generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), + generate_build_id_(false), implicit_null_checks_(implicit_null_checks), implicit_so_checks_(implicit_so_checks), implicit_suspend_checks_(implicit_suspend_checks), @@ -196,6 +198,10 @@ bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usa generate_mini_debug_info_ = true; } else if (option == "--no-generate-mini-debug-info") { generate_mini_debug_info_ = false; + } else if (option == "--generate-build-id") { + generate_build_id_ = true; + } else if (option == "--no-generate-build-id") { + generate_build_id_ = false; } else if (option == "--debuggable") { debuggable_ = true; } else if (option.starts_with("--top-k-profile-threshold=")) { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 8e4a775558..3c920d9601 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -187,6 +187,10 @@ class CompilerOptions FINAL { return generate_mini_debug_info_; } + bool GetGenerateBuildId() const { + return generate_build_id_; + } + bool GetImplicitNullChecks() const { return implicit_null_checks_; } @@ -297,6 +301,7 @@ class CompilerOptions FINAL { bool debuggable_; bool generate_debug_info_; bool generate_mini_debug_info_; + bool generate_build_id_; bool implicit_null_checks_; bool implicit_so_checks_; bool implicit_suspend_checks_; diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 73240bed03..31a75294bc 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -36,6 +36,7 @@ namespace art { // The basic layout of the elf file: // Elf_Ehdr - The ELF header. // Elf_Phdr[] - Program headers for the linker. +// .note.gnu.build-id - Optional build ID section (SHA-1 digest). // .rodata - DEX files and oat metadata. // .text - Compiled code. // .bss - Zero-initialized writeable section. @@ -75,6 +76,10 @@ template <typename ElfTypes> class ElfBuilder FINAL { public: static constexpr size_t kMaxProgramHeaders = 16; + // SHA-1 digest. Not using SHA_DIGEST_LENGTH from openssl/sha.h to avoid + // spreading this header dependency for just this single constant. + static constexpr size_t kBuildIdLen = 20; + using Elf_Addr = typename ElfTypes::Addr; using Elf_Off = typename ElfTypes::Off; using Elf_Word = typename ElfTypes::Word; @@ -458,6 +463,49 @@ class ElfBuilder FINAL { } abiflags_; }; + class BuildIdSection FINAL : public Section { + public: + BuildIdSection(ElfBuilder<ElfTypes>* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize) + : Section(owner, name, type, flags, link, info, align, entsize), + digest_start_(-1) { + } + + void Write() { + // The size fields are 32-bit on both 32-bit and 64-bit systems, confirmed + // with the 64-bit linker and libbfd code. The size of name and desc must + // be a multiple of 4 and it currently is. + this->WriteUint32(4); // namesz. + this->WriteUint32(kBuildIdLen); // descsz. + this->WriteUint32(3); // type = NT_GNU_BUILD_ID. + this->WriteFully("GNU", 4); // name. + digest_start_ = this->Seek(0, kSeekCurrent); + static_assert(kBuildIdLen % 4 == 0, "expecting a mutliple of 4 for build ID length"); + this->WriteFully(std::string(kBuildIdLen, '\0').c_str(), kBuildIdLen); // desc. + } + + off_t GetDigestStart() { + CHECK_GT(digest_start_, 0); + return digest_start_; + } + + private: + bool WriteUint32(uint32_t v) { + return this->WriteFully(&v, sizeof(v)); + } + + // File offset where the build ID digest starts. + // Populated with zeros first, then updated with the actual value as the + // very last thing in the output file creation. + off_t digest_start_; + }; + ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output) : isa_(isa), features_(features), @@ -479,6 +527,7 @@ class ElfBuilder FINAL { shstrtab_(this, ".shstrtab", 0, 1), abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0, isa, features), + build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0), started_(false), write_program_headers_(false), loaded_size_(0u), @@ -489,6 +538,7 @@ class ElfBuilder FINAL { dynamic_.phdr_type_ = PT_DYNAMIC; eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME; abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS; + build_id_.phdr_type_ = PT_NOTE; } ~ElfBuilder() {} @@ -741,6 +791,17 @@ class ElfBuilder FINAL { abiflags_.End(); } + void WriteBuildIdSection() { + build_id_.Start(); + build_id_.Write(); + build_id_.End(); + } + + void WriteBuildId(uint8_t build_id[kBuildIdLen]) { + stream_.Seek(build_id_.GetDigestStart(), kSeekSet); + stream_.WriteFully(build_id, kBuildIdLen); + } + // Returns true if all writes and seeks on the output stream succeeded. bool Good() { return stream_.Good(); @@ -932,6 +993,7 @@ class ElfBuilder FINAL { Section debug_line_; StringSection shstrtab_; AbiflagsSection abiflags_; + BuildIdSection build_id_; std::vector<std::unique_ptr<Section>> other_sections_; // List of used section in the order in which they were written. diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 36cd2327c4..0d6575cffd 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -16,6 +16,7 @@ #include "elf_writer_quick.h" +#include <openssl/sha.h> #include <unordered_map> #include <unordered_set> @@ -126,6 +127,8 @@ class ElfWriterQuick FINAL : public ElfWriter { std::unique_ptr<DebugInfoTask> debug_info_task_; std::unique_ptr<ThreadPool> debug_info_thread_pool_; + void ComputeFileBuildId(uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]); + DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick); }; @@ -167,6 +170,9 @@ ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {} template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::Start() { builder_->Start(); + if (compiler_options_->GetGenerateBuildId()) { + builder_->WriteBuildIdSection(); + } } template <typename ElfTypes> @@ -275,11 +281,36 @@ void ElfWriterQuick<ElfTypes>::WritePatchLocations( template <typename ElfTypes> bool ElfWriterQuick<ElfTypes>::End() { builder_->End(); - + if (compiler_options_->GetGenerateBuildId()) { + uint8_t build_id[ElfBuilder<ElfTypes>::kBuildIdLen]; + ComputeFileBuildId(&build_id); + builder_->WriteBuildId(build_id); + } return builder_->Good(); } template <typename ElfTypes> +void ElfWriterQuick<ElfTypes>::ComputeFileBuildId( + uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]) { + constexpr int kBufSize = 8192; + std::vector<char> buffer(kBufSize); + int64_t offset = 0; + SHA_CTX ctx; + SHA1_Init(&ctx); + while (true) { + int64_t bytes_read = elf_file_->Read(buffer.data(), kBufSize, offset); + CHECK_GE(bytes_read, 0); + if (bytes_read == 0) { + // End of file. + break; + } + SHA1_Update(&ctx, buffer.data(), bytes_read); + offset += bytes_read; + } + SHA1_Final(*build_id, &ctx); +} + +template <typename ElfTypes> OutputStream* ElfWriterQuick<ElfTypes>::GetStream() { return builder_->GetStream(); } diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index d5f16637be..b58004976e 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -101,6 +101,25 @@ TEST_F(ElfWriterTest, dlsym) { } } +TEST_F(ElfWriterTest, CheckBuildIdPresent) { + std::string elf_location = GetCoreOatLocation(); + std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA); + LOG(INFO) << "elf_filename=" << elf_filename; + + std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str())); + ASSERT_TRUE(file.get() != nullptr); + { + std::string error_msg; + std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), + false, + false, + /*low_4gb*/false, + &error_msg)); + CHECK(ef.get() != nullptr) << error_msg; + EXPECT_TRUE(ef->HasSection(".note.gnu.build-id")); + } +} + TEST_F(ElfWriterTest, EncodeDecodeOatPatches) { const std::vector<std::vector<uintptr_t>> test_data { { 0, 4, 8, 15, 128, 200 }, diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 8ae04a1e49..13c73dcf42 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1389,7 +1389,7 @@ void ImageWriter::ProcessWorkStack(WorkStack* work_stack) { void ImageWriter::CalculateNewObjectOffsets() { Thread* const self = Thread::Current(); - StackHandleScopeCollection handles(self); + VariableSizedHandleScope handles(self); std::vector<Handle<ObjectArray<Object>>> image_roots; for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { image_roots.push_back(handles.NewHandle(CreateImageRoots(i))); diff --git a/compiler/intrinsics_list.h b/compiler/intrinsics_list.h index 5877f57b74..b617387ef8 100644 --- a/compiler/intrinsics_list.h +++ b/compiler/intrinsics_list.h @@ -1,7 +1,7 @@ /* - * Copyright (C, "", "", "") 2015 The Android Open Source Project + * Copyright (C) 2015 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License", "", "", ""); + * 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 * diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 0cf5f18c4b..afb8fce8d7 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -530,7 +530,7 @@ struct ScopedCheckHandleScope { << "invocations have finished (as before they were invoked)."; } - HandleScope* const handle_scope_; + BaseHandleScope* const handle_scope_; }; static void expectNumStackReferences(size_t val1, size_t val2) { diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 7e58d789d0..bfb342f966 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -322,7 +322,7 @@ static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, ThreadOffset<kPointerSize> jni_start = is_synchronized ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStartSynchronized) - : (is_fast_native + : ((is_fast_native && !reference_return) // TODO: support @FastNative returning obj ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastStart) : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart)); diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 580ef72767..f896f1199e 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -43,7 +43,7 @@ class HGraphBuilder : public ValueObject { OptimizingCompilerStats* compiler_stats, const uint8_t* interpreter_metadata, Handle<mirror::DexCache> dex_cache, - StackHandleScopeCollection* handles) + VariableSizedHandleScope* handles) : graph_(graph), dex_file_(dex_file), code_item_(code_item), @@ -68,7 +68,7 @@ class HGraphBuilder : public ValueObject { // Only for unit testing. HGraphBuilder(HGraph* graph, const DexFile::CodeItem& code_item, - StackHandleScopeCollection* handles, + VariableSizedHandleScope* handles, Primitive::Type return_type = Primitive::kPrimInt) : graph_(graph), dex_file_(nullptr), diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 9870876879..77d6f23fff 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1129,7 +1129,13 @@ void CodeGeneratorARM::GenerateFrameEntry() { int adjust = GetFrameSize() - FrameEntrySpillSize(); __ AddConstant(SP, -adjust); __ cfi().AdjustCFAOffset(adjust); - __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0); + + // Save the current method if we need it. Note that we do not + // do this in HCurrentMethod, as the instruction might have been removed + // in the SSA graph. + if (RequiresCurrentMethod()) { + __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0); + } } void CodeGeneratorARM::GenerateFrameExit() { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 969d653f97..b1e4f5c763 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1046,7 +1046,13 @@ void CodeGeneratorARM64::GenerateFrameEntry() { // ... : other preserved fp registers. // ... : reserved frame space. // sp[0] : current method. - __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex)); + + // Save the current method if we need it. Note that we do not + // do this in HCurrentMethod, as the instruction might have been removed + // in the SSA graph. + if (RequiresCurrentMethod()) { + __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex)); + } GetAssembler()->cfi().AdjustCFAOffset(frame_size); GetAssembler()->SpillRegisters(GetFramePreservedCoreRegisters(), frame_size - GetCoreSpillSize()); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 990bbcc85b..bc8bb480ec 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -743,9 +743,12 @@ void CodeGeneratorMIPS::GenerateFrameEntry() { // TODO: __ cfi().RelOffset(DWARFReg(reg), ofs); } - // Store the current method pointer. - // TODO: can we not do this if RequiresCurrentMethod() returns false? - __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset); + // Save the current method if we need it. Note that we do not + // do this in HCurrentMethod, as the instruction might have been removed + // in the SSA graph. + if (RequiresCurrentMethod()) { + __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset); + } } void CodeGeneratorMIPS::GenerateFrameExit() { diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 02576bda67..010bf24232 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -556,9 +556,14 @@ void CodeGeneratorMIPS64::GenerateFrameEntry() { __ IncreaseFrameSize(GetFrameSize() - FrameEntrySpillSize()); - static_assert(IsInt<16>(kCurrentMethodStackOffset), - "kCurrentMethodStackOffset must fit into int16_t"); - __ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset); + // Save the current method if we need it. Note that we do not + // do this in HCurrentMethod, as the instruction might have been removed + // in the SSA graph. + if (RequiresCurrentMethod()) { + static_assert(IsInt<16>(kCurrentMethodStackOffset), + "kCurrentMethodStackOffset must fit into int16_t"); + __ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset); + } } void CodeGeneratorMIPS64::GenerateFrameExit() { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 0b23599665..960f01ce9d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -898,7 +898,12 @@ void CodeGeneratorX86::GenerateFrameEntry() { int adjust = GetFrameSize() - FrameEntrySpillSize(); __ subl(ESP, Immediate(adjust)); __ cfi().AdjustCFAOffset(adjust); - __ movl(Address(ESP, kCurrentMethodStackOffset), kMethodRegisterArgument); + // Save the current method if we need it. Note that we do not + // do this in HCurrentMethod, as the instruction might have been removed + // in the SSA graph. + if (RequiresCurrentMethod()) { + __ movl(Address(ESP, kCurrentMethodStackOffset), kMethodRegisterArgument); + } } void CodeGeneratorX86::GenerateFrameExit() { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 28638d721d..665d028338 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1140,8 +1140,13 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { } } - __ movq(Address(CpuRegister(RSP), kCurrentMethodStackOffset), - CpuRegister(kMethodRegisterArgument)); + // Save the current method if we need it. Note that we do not + // do this in HCurrentMethod, as the instruction might have been removed + // in the SSA graph. + if (RequiresCurrentMethod()) { + __ movq(Address(CpuRegister(RSP), kCurrentMethodStackOffset), + CpuRegister(kMethodRegisterArgument)); + } } void CodeGeneratorX86_64::GenerateFrameExit() { diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index c501ccf80f..55fcb12fa8 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -87,11 +87,12 @@ HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph) : HOptimization(graph, kInductionPassName), global_depth_(0), stack_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)), - scc_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)), map_(std::less<HInstruction*>(), graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)), + scc_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)), cycle_(std::less<HInstruction*>(), graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)), + type_(Primitive::kPrimVoid), induction_(std::less<HLoopInformation*>(), graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)) { } @@ -103,7 +104,6 @@ void HInductionVarAnalysis::Run() { for (HReversePostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) { HBasicBlock* graph_block = it_graph.Current(); // Don't analyze irreducible loops. - // TODO(ajcbik): could/should we remove this restriction? if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) { VisitLoop(graph_block->GetLoopInformation()); } @@ -121,7 +121,7 @@ void HInductionVarAnalysis::VisitLoop(HLoopInformation* loop) { HBasicBlock* loop_block = it_loop.Current(); DCHECK(loop_block->IsInLoop()); if (loop_block->GetLoopInformation() != loop) { - continue; // Inner loops already visited. + continue; // Inner loops visited later. } // Visit phi-operations and instructions. for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) { @@ -285,6 +285,9 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { } else if (instruction->IsSub()) { update = SolveAddSub( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true); + } else if (instruction->IsXor()) { + update = SolveXor( + loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), true); } else if (instruction->IsTypeConversion()) { update = SolveCnv(instruction->AsTypeConversion()); } @@ -553,6 +556,27 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn return nullptr; } +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveXor(HLoopInformation* loop, + HInstruction* entry_phi, + HInstruction* instruction, + HInstruction* x, + HInstruction* y, + bool is_first_call) { + InductionInfo* b = LookupInfo(loop, y); + // Solve within a tight cycle on x = x ^ c. + if (b != nullptr && b->induction_class == kInvariant) { + if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) { + InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); + return CreateInduction(kPeriodic, CreateInvariantOp(kXor, initial, b), initial, type_); + } + } + // Try the other way around if considered for first time. + if (is_first_call) { + return SolveXor(loop, entry_phi, instruction, y, x, false); + } + return nullptr; +} + HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) { Primitive::Type from = conversion->GetInputType(); Primitive::Type to = conversion->GetResultType(); @@ -850,8 +874,8 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv int64_t value = -1; if (IsExact(a, &value)) { if (value == 0) { - // Simplify 0 + b = b, 0 * b = 0. - if (op == kAdd) { + // Simplify 0 + b = b, 0 ^ b = b, 0 * b = 0. + if (op == kAdd || op == kXor) { return b; } else if (op == kMul) { return a; @@ -867,8 +891,8 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv } if (IsExact(b, &value)) { if (value == 0) { - // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0. - if (op == kAdd || op == kSub) { + // Simplify a + 0 = a, a - 0 = a, a ^ 0 = a, a * 0 = 0, -0 = 0. + if (op == kAdd || op == kSub || op == kXor) { return a; } else if (op == kMul || op == kNeg) { return b; @@ -939,6 +963,7 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) { case kNeg: inv += " - "; break; case kMul: inv += " * "; break; case kDiv: inv += " / "; break; + case kXor: inv += " ^ "; break; case kLT: inv += " < "; break; case kLE: inv += " <= "; break; case kGT: inv += " > "; break; diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index cd4c830645..06aee31b88 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -64,6 +64,7 @@ class HInductionVarAnalysis : public HOptimization { kNeg, kMul, kDiv, + kXor, kFetch, // Trip-counts. kTripCountInLoop, // valid in full loop; loop is finite @@ -171,7 +172,13 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* x, HInstruction* y, InductionOp op, - bool is_first_call); + bool is_first_call); // possibly swaps x and y to try again + InductionInfo* SolveXor(HLoopInformation* loop, + HInstruction* entry_phi, + HInstruction* instruction, + HInstruction* x, + HInstruction* y, + bool is_first_call); // possibly swaps x and y to try again InductionInfo* SolveCnv(HTypeConversion* conversion); // Trip count information. @@ -219,8 +226,8 @@ class HInductionVarAnalysis : public HOptimization { // Temporary book-keeping during the analysis. uint32_t global_depth_; ArenaVector<HInstruction*> stack_; - ArenaVector<HInstruction*> scc_; ArenaSafeMap<HInstruction*, NodeInfo> map_; + ArenaVector<HInstruction*> scc_; ArenaSafeMap<HInstruction*, InductionInfo*> cycle_; Primitive::Type type_; diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 292bc4e06e..7c467f6c94 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -107,7 +107,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { } // Builds if-statement at depth d. - HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) { + HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) { HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_); HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_); HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_); @@ -259,15 +259,15 @@ TEST_F(InductionVarAnalysisTest, FindDerivedInduction) { // k = - i; // } BuildLoopNest(1); - HInstruction *add = InsertInstruction( + HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0); - HInstruction *mul = InsertInstruction( + HInstruction* mul = InsertInstruction( new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0); - HInstruction *shl = InsertInstruction( + HInstruction* shl = InsertInstruction( new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0); - HInstruction *neg = InsertInstruction( + HInstruction* neg = InsertInstruction( new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0); PerformInductionVarAnalysis(); @@ -291,10 +291,10 @@ TEST_F(InductionVarAnalysisTest, FindChainInduction) { HPhi* k = InsertLoopPhi(0, 0); k->AddInput(constant0_); - HInstruction *add = InsertInstruction( + HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0); HInstruction* store1 = InsertArrayStore(add, 0); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0); HInstruction* store2 = InsertArrayStore(sub, 0); k->AddInput(sub); @@ -381,7 +381,7 @@ TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) { k->AddInput(constant0_); HInstruction* store = InsertArrayStore(k, 0); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0); k->AddInput(sub); PerformInductionVarAnalysis(); @@ -407,7 +407,7 @@ TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) { HInstruction* store = InsertArrayStore(k, 0); k->AddInput(t); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0); t->AddInput(sub); PerformInductionVarAnalysis(); @@ -431,15 +431,15 @@ TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) { HPhi* k = InsertLoopPhi(0, 0); k->AddInput(constant0_); - HInstruction *add = InsertInstruction( + HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0); - HInstruction *mul = InsertInstruction( + HInstruction* mul = InsertInstruction( new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0); - HInstruction *shl = InsertInstruction( + HInstruction* shl = InsertInstruction( new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0); - HInstruction *neg = InsertInstruction( + HInstruction* neg = InsertInstruction( new (&allocator_) HNeg(Primitive::kPrimInt, k), 0); k->AddInput( InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0)); @@ -497,7 +497,7 @@ TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) { k->AddInput(constant0_); HInstruction* store = InsertArrayStore(k, 0); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0); k->AddInput(sub); PerformInductionVarAnalysis(); @@ -506,6 +506,45 @@ TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) { EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str()); } +TEST_F(InductionVarAnalysisTest, FindXorPeriodicInduction) { + // Setup: + // k = 0; + // for (int i = 0; i < 100; i++) { + // a[k] = 0; + // k = k ^ 1; + // } + BuildLoopNest(1); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + + HInstruction* store = InsertArrayStore(k, 0); + HInstruction* x = InsertInstruction( + new (&allocator_) HXor(Primitive::kPrimInt, k, constant1_), 0); + k->AddInput(x); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); + EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(x, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) { + // Setup: + // k = 100; + // for (int i = 0; i < 100; i++) { + // k = k ^ 100; + // } + BuildLoopNest(1); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant100_); + + HInstruction* x = InsertInstruction( + new (&allocator_) HXor(Primitive::kPrimInt, k, constant100_), 0); + k->AddInput(x); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("periodic(((100) ^ (100)), (100)):PrimInt", GetInductionInfo(x, 0).c_str()); +} + TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { // Setup: // k = 0; @@ -526,15 +565,15 @@ TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { k_header->AddInput(k_body); // Derived expressions. - HInstruction *add = InsertInstruction( + HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0); - HInstruction *sub = InsertInstruction( + HInstruction* sub = InsertInstruction( new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0); - HInstruction *mul = InsertInstruction( + HInstruction* mul = InsertInstruction( new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0); - HInstruction *shl = InsertInstruction( + HInstruction* shl = InsertInstruction( new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0); - HInstruction *neg = InsertInstruction( + HInstruction* neg = InsertInstruction( new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0); PerformInductionVarAnalysis(); @@ -563,7 +602,7 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { k[d] = InsertLoopPhi(0, d); } - HInstruction *inc = InsertInstruction( + HInstruction* inc = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9); HInstruction* store = InsertArrayStore(inc, 9); @@ -597,7 +636,7 @@ TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) { // a[i] = 0; // } BuildLoopNest(1); - HInstruction *conv = InsertInstruction( + HInstruction* conv = InsertInstruction( new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0); HInstruction* store1 = InsertArrayStore(conv, 0); HInstruction* store2 = InsertArrayStore(basic_[0], 0); diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index cd8b7c7960..140c7f0c40 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -525,6 +525,8 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct return GetMul(info->op_a, info->op_b, trip, in_body, is_min); case HInductionVarAnalysis::kDiv: return GetDiv(info->op_a, info->op_b, trip, in_body, is_min); + case HInductionVarAnalysis::kXor: + return GetXor(info->op_a, info->op_b); case HInductionVarAnalysis::kFetch: return GetFetch(info->fetch, trip, in_body, is_min); case HInductionVarAnalysis::kTripCountInLoop: @@ -626,6 +628,21 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct return Value(); } +InductionVarRange::Value InductionVarRange::GetXor( + HInductionVarAnalysis::InductionInfo* info1, + HInductionVarAnalysis::InductionInfo* info2) const { + int64_t v1 = 0; + int64_t v2 = 0; + // Only accept exact values. + if (IsConstant(info1, kExact, &v1) && IsConstant(info2, kExact, &v2)) { + int64_t value = v1 ^ v2; + if (CanLongValueFitIntoInt(value)) { + return Value(static_cast<int32_t>(value)); + } + } + return Value(); +} + InductionVarRange::Value InductionVarRange::MulRangeAndConstant( int64_t value, HInductionVarAnalysis::InductionInfo* info, diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index df31e81169..895130064a 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -193,6 +193,8 @@ class InductionVarRange { HInductionVarAnalysis::InductionInfo* trip, bool in_body, bool is_min) const; + Value GetXor(HInductionVarAnalysis::InductionInfo* info1, + HInductionVarAnalysis::InductionInfo* info2) const; Value MulRangeAndConstant(int64_t value, HInductionVarAnalysis::InductionInfo* info, diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 486626b1fe..a1dcd58a84 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -38,7 +38,7 @@ class HInliner : public HOptimization { const DexCompilationUnit& outer_compilation_unit, const DexCompilationUnit& caller_compilation_unit, CompilerDriver* compiler_driver, - StackHandleScopeCollection* handles, + VariableSizedHandleScope* handles, OptimizingCompilerStats* stats, size_t total_number_of_dex_registers, size_t depth) @@ -197,7 +197,7 @@ class HInliner : public HOptimization { const size_t total_number_of_dex_registers_; const size_t depth_; size_t number_of_inlined_instructions_; - StackHandleScopeCollection* const handles_; + VariableSizedHandleScope* const handles_; DISALLOW_COPY_AND_ASSIGN(HInliner); }; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 874c1edf35..1e69966b98 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -35,7 +35,7 @@ namespace art { // double). static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == 0); -void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) { +void HGraph::InitializeInexactObjectRTI(VariableSizedHandleScope* handles) { ScopedObjectAccess soa(Thread::Current()); // Create the inexact Object reference type and store it in the HGraph. ClassLinker* linker = Runtime::Current()->GetClassLinker(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 348f99d6df..6f4f3c9505 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -336,7 +336,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { } // Acquires and stores RTI of inexact Object to be used when creating HNullConstant. - void InitializeInexactObjectRTI(StackHandleScopeCollection* handles); + void InitializeInexactObjectRTI(VariableSizedHandleScope* handles); ArenaAllocator* GetArena() const { return arena_; } const ArenaVector<HBasicBlock*>& GetBlocks() const { return blocks_; } @@ -2093,10 +2093,10 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // to the current method. Such instructions are: // (1): Instructions that require an environment, as calling the runtime requires // to walk the stack and have the current method stored at a specific stack address. - // (2): Object literals like classes and strings, that are loaded from the dex cache - // fields of the current method. + // (2): HCurrentMethod, potentially used by HInvokeStaticOrDirect, HLoadString, or HLoadClass + // to access the dex cache. bool NeedsCurrentMethod() const { - return NeedsEnvironment() || IsLoadClass() || IsLoadString(); + return NeedsEnvironment() || IsCurrentMethod(); } // Returns whether the code generation of the instruction will require to have access diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index d6f8307ac2..4370a84bd2 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -319,7 +319,7 @@ class OptimizingCompiler FINAL : public Compiler { CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - StackHandleScopeCollection* handles) const; + VariableSizedHandleScope* handles) const; void RunOptimizations(HOptimization* optimizations[], size_t length, @@ -358,7 +358,7 @@ class OptimizingCompiler FINAL : public Compiler { CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - StackHandleScopeCollection* handles) const; + VariableSizedHandleScope* handles) const; void RunArchOptimizations(InstructionSet instruction_set, HGraph* graph, @@ -442,7 +442,7 @@ static HOptimization* BuildOptimization( CodeGenerator* codegen, CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, - StackHandleScopeCollection* handles, + VariableSizedHandleScope* handles, SideEffectsAnalysis* most_recent_side_effects, HInductionVarAnalysis* most_recent_induction) { std::string opt_name = ConvertPassNameToOptimizationName(pass_name); @@ -524,7 +524,7 @@ static ArenaVector<HOptimization*> BuildOptimizations( CodeGenerator* codegen, CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, - StackHandleScopeCollection* handles) { + VariableSizedHandleScope* handles) { // Few HOptimizations constructors require SideEffectsAnalysis or HInductionVarAnalysis // instances. This method assumes that each of them expects the nearest instance preceeding it // in the pass name list. @@ -570,7 +570,7 @@ void OptimizingCompiler::MaybeRunInliner(HGraph* graph, CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - StackHandleScopeCollection* handles) const { + VariableSizedHandleScope* handles) const { OptimizingCompilerStats* stats = compilation_stats_.get(); const CompilerOptions& compiler_options = driver->GetCompilerOptions(); bool should_inline = (compiler_options.GetInlineDepthLimit() > 0) @@ -707,7 +707,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - StackHandleScopeCollection* handles) const { + VariableSizedHandleScope* handles) const { OptimizingCompilerStats* stats = compilation_stats_.get(); ArenaAllocator* arena = graph->GetArena(); if (driver->GetCompilerOptions().GetPassesToRun() != nullptr) { @@ -949,7 +949,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, { ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); + VariableSizedHandleScope handles(soa.Self()); // Do not hold `mutator_lock_` between optimizations. ScopedThreadSuspension sts(soa.Self(), kNative); diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 2a23c92f1f..58d90176cd 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -90,7 +90,7 @@ inline HGraph* CreateCFG(ArenaAllocator* allocator, { ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); + VariableSizedHandleScope handles(soa.Self()); HGraphBuilder builder(graph, *item, &handles, return_type); bool graph_built = (builder.BuildGraph() == kAnalysisSuccess); return graph_built ? graph : nullptr; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 45a3ce411e..83698adba4 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -35,7 +35,7 @@ static inline mirror::DexCache* FindDexCacheWithHint(Thread* self, } } -static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollection* handles, +static inline ReferenceTypeInfo::TypeHandle GetRootHandle(VariableSizedHandleScope* handles, ClassLinker::ClassRoot class_root, ReferenceTypeInfo::TypeHandle* cache) { if (!ReferenceTypeInfo::IsValidHandle(*cache)) { @@ -109,7 +109,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, Handle<mirror::DexCache> hint_dex_cache, - StackHandleScopeCollection* handles, + VariableSizedHandleScope* handles, bool is_first_run, const char* name) : HOptimization(graph, name), diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 61428b2a45..4663471729 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -34,7 +34,7 @@ class ReferenceTypePropagation : public HOptimization { public: ReferenceTypePropagation(HGraph* graph, Handle<mirror::DexCache> hint_dex_cache, - StackHandleScopeCollection* handles, + VariableSizedHandleScope* handles, bool is_first_run, const char* name = kReferenceTypePropagationPassName); @@ -56,7 +56,7 @@ class ReferenceTypePropagation : public HOptimization { private: class HandleCache { public: - explicit HandleCache(StackHandleScopeCollection* handles) : handles_(handles) { } + explicit HandleCache(VariableSizedHandleScope* handles) : handles_(handles) { } template <typename T> MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -74,7 +74,7 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); private: - StackHandleScopeCollection* handles_; + VariableSizedHandleScope* handles_; ReferenceTypeInfo::TypeHandle object_class_handle_; ReferenceTypeInfo::TypeHandle class_class_handle_; diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc index 75a4eac538..b061c871b0 100644 --- a/compiler/optimizing/reference_type_propagation_test.cc +++ b/compiler/optimizing/reference_type_propagation_test.cc @@ -35,7 +35,7 @@ class ReferenceTypePropagationTest : public CommonCompilerTest { ~ReferenceTypePropagationTest() { } - void SetupPropagation(StackHandleScopeCollection* handles) { + void SetupPropagation(VariableSizedHandleScope* handles) { graph_->InitializeInexactObjectRTI(handles); propagation_ = new (&allocator_) ReferenceTypePropagation(graph_, Handle<mirror::DexCache>(), @@ -79,7 +79,7 @@ class ReferenceTypePropagationTest : public CommonCompilerTest { TEST_F(ReferenceTypePropagationTest, ProperSetup) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); + VariableSizedHandleScope handles(soa.Self()); SetupPropagation(&handles); EXPECT_TRUE(propagation_ != nullptr); @@ -88,7 +88,7 @@ TEST_F(ReferenceTypePropagationTest, ProperSetup) { TEST_F(ReferenceTypePropagationTest, MergeInvalidTypes) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); + VariableSizedHandleScope handles(soa.Self()); SetupPropagation(&handles); // Two invalid types. @@ -120,7 +120,7 @@ TEST_F(ReferenceTypePropagationTest, MergeInvalidTypes) { TEST_F(ReferenceTypePropagationTest, MergeValidTypes) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); + VariableSizedHandleScope handles(soa.Self()); SetupPropagation(&handles); // Same types. diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index d7360adef8..45dac54115 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -49,7 +49,7 @@ class SsaBuilder : public ValueObject { public: SsaBuilder(HGraph* graph, Handle<mirror::DexCache> dex_cache, - StackHandleScopeCollection* handles) + VariableSizedHandleScope* handles) : graph_(graph), dex_cache_(dex_cache), handles_(handles), @@ -116,7 +116,7 @@ class SsaBuilder : public ValueObject { HGraph* graph_; Handle<mirror::DexCache> dex_cache_; - StackHandleScopeCollection* const handles_; + VariableSizedHandleScope* const handles_; // True if types of ambiguous ArrayGets have been resolved. bool agets_fixed_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 8f3f115ae7..e26fa7ff03 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -339,6 +339,11 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --no-generate-mini-debug-info: Do not generate backtrace info."); UsageError(""); + UsageError(" --generate-build-id: Generate GNU-compatible linker build ID ELF section with"); + UsageError(" SHA-1 of the file content (and thus stable across identical builds)"); + UsageError(""); + UsageError(" --no-generate-build-id: Do not generate the build ID ELF section."); + UsageError(""); UsageError(" --debuggable: Produce code debuggable with Java debugger."); UsageError(""); UsageError(" --runtime-arg <argument>: used to specify various arguments for the runtime,"); diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 8a51dc2c04..5ec989831e 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -274,7 +274,7 @@ class ScopedCheck { AbortF("field operation on NULL object: %p", java_object); return false; } - if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o.Ptr())) { Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR)); AbortF("field operation on invalid %s: %p", ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), @@ -782,7 +782,7 @@ class ScopedCheck { } } - if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj.Ptr())) { Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR)); AbortF("%s is an invalid %s: %p (%p)", what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), @@ -939,7 +939,7 @@ class ScopedCheck { ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc); if (c == nullptr) { *msg += "NULL"; - } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) { + } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c.Ptr())) { StringAppendF(msg, "INVALID POINTER:%p", jc); } else if (!c->IsClass()) { *msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c); @@ -1108,7 +1108,7 @@ class ScopedCheck { } ObjPtr<mirror::Array> a = soa.Decode<mirror::Array>(java_array); - if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) { + if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a.Ptr()))) { Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR)); AbortF("jarray is an invalid %s: %p (%p)", ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(), @@ -1145,7 +1145,7 @@ class ScopedCheck { } ArtField* f = soa.DecodeField(fid); // TODO: Better check here. - if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass())) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass().Ptr())) { Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR)); AbortF("invalid jfieldID: %p", fid); return nullptr; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 7006f70687..ada1a237d6 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -892,15 +892,16 @@ JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class std::vector<uint64_t>* counts) { gc::Heap* heap = Runtime::Current()->GetHeap(); heap->CollectGarbage(false); - std::vector<mirror::Class*> classes; + VariableSizedHandleScope hs(Thread::Current()); + std::vector<Handle<mirror::Class>> classes; counts->clear(); for (size_t i = 0; i < class_ids.size(); ++i) { JDWP::JdwpError error; - mirror::Class* c = DecodeClass(class_ids[i], &error); + ObjPtr<mirror::Class> c = DecodeClass(class_ids[i], &error); if (c == nullptr) { return error; } - classes.push_back(c); + classes.push_back(hs.NewHandle(c)); counts->push_back(0); } heap->CountInstances(classes, false, &(*counts)[0]); @@ -913,14 +914,15 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, // We only want reachable instances, so do a GC. heap->CollectGarbage(false); JDWP::JdwpError error; - mirror::Class* c = DecodeClass(class_id, &error); + ObjPtr<mirror::Class> c = DecodeClass(class_id, &error); if (c == nullptr) { return error; } - std::vector<mirror::Object*> raw_instances; - Runtime::Current()->GetHeap()->GetInstances(c, max_count, raw_instances); + VariableSizedHandleScope hs(Thread::Current()); + std::vector<Handle<mirror::Object>> raw_instances; + Runtime::Current()->GetHeap()->GetInstances(hs, hs.NewHandle(c), max_count, raw_instances); for (size_t i = 0; i < raw_instances.size(); ++i) { - instances->push_back(gRegistry->Add(raw_instances[i])); + instances->push_back(gRegistry->Add(raw_instances[i].Get())); } return JDWP::ERR_NONE; } @@ -930,14 +932,15 @@ JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_c gc::Heap* heap = Runtime::Current()->GetHeap(); heap->CollectGarbage(false); JDWP::JdwpError error; - mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error); + ObjPtr<mirror::Object> o = gRegistry->Get<mirror::Object*>(object_id, &error); if (o == nullptr) { return JDWP::ERR_INVALID_OBJECT; } - std::vector<mirror::Object*> raw_instances; - heap->GetReferringObjects(o, max_count, raw_instances); + VariableSizedHandleScope hs(Thread::Current()); + std::vector<Handle<mirror::Object>> raw_instances; + heap->GetReferringObjects(hs, hs.NewHandle(o), max_count, raw_instances); for (size_t i = 0; i < raw_instances.size(); ++i) { - referring_objects->push_back(gRegistry->Add(raw_instances[i])); + referring_objects->push_back(gRegistry->Add(raw_instances[i].Get())); } return JDWP::ERR_NONE; } diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 7c7e2da740..20fa0d8e6b 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -144,7 +144,7 @@ static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&o)); CheckReferenceResult(h_obj, self); } - VerifyObject(o.Ptr()); + VerifyObject(o); return o.Ptr(); } diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h index 6fb74d3313..f60bc0c834 100644 --- a/runtime/gc/allocation_listener.h +++ b/runtime/gc/allocation_listener.h @@ -22,6 +22,7 @@ #include "base/macros.h" #include "base/mutex.h" +#include "obj_ptr.h" #include "object_callbacks.h" #include "gc_root.h" @@ -39,7 +40,7 @@ class AllocationListener { public: virtual ~AllocationListener() {} - virtual void ObjectAllocated(Thread* self, mirror::Object** obj, size_t byte_count) + virtual void ObjectAllocated(Thread* self, ObjPtr<mirror::Object>* obj, size_t byte_count) REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 13ebb272c5..d921900933 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -19,6 +19,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/stl_util.h" +#include "obj_ptr-inl.h" #include "stack.h" #ifdef ART_TARGET_ANDROID @@ -263,7 +264,7 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) { } void AllocRecordObjectMap::RecordAllocation(Thread* self, - mirror::Object** obj, + ObjPtr<mirror::Object>* obj, size_t byte_count) { // Get stack trace outside of lock in case there are allocations during the stack walk. // b/27858645. @@ -305,7 +306,7 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self, trace.SetTid(self->GetTid()); // Add the record. - Put(*obj, AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace))); + Put(obj->Ptr(), AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace))); DCHECK_LE(Size(), alloc_record_max_); } diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h index f1f013b3b5..c8b2b89702 100644 --- a/runtime/gc/allocation_record.h +++ b/runtime/gc/allocation_record.h @@ -21,6 +21,7 @@ #include <memory> #include "base/mutex.h" +#include "obj_ptr.h" #include "object_callbacks.h" #include "gc_root.h" @@ -210,7 +211,7 @@ class AllocRecordObjectMap { // Caller needs to check that it is enabled before calling since we read the stack trace before // checking the enabled boolean. void RecordAllocation(Thread* self, - mirror::Object** obj, + ObjPtr<mirror::Object>* obj, size_t byte_count) REQUIRES(!Locks::alloc_tracker_lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 2e971729f0..76a478ef1b 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -296,7 +296,6 @@ class SemiSpace::VerifyNoFromSpaceReferencesVisitor { REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE { mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset); if (from_space_->HasAddress(ref)) { - Runtime::Current()->GetHeap()->DumpObject(LOG_STREAM(INFO), obj); LOG(FATAL) << ref << " found in from space"; } } diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 00adefb095..05ce9c7952 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -41,7 +41,7 @@ namespace gc { template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor> inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, - mirror::Class* klass, + ObjPtr<mirror::Class> klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) { @@ -52,16 +52,19 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, CHECK_EQ(self->GetState(), kRunnable); self->AssertThreadSuspensionIsAllowable(); self->AssertNoPendingException(); + // Make sure to preserve klass. + StackHandleScope<1> hs(self); + HandleWrapperObjPtr<mirror::Class> h = hs.NewHandleWrapper(&klass); self->PoisonObjectPointers(); } // Need to check that we arent the large object allocator since the large object allocation code // path this function. If we didn't check we would have an infinite loop. - mirror::Object* obj; + ObjPtr<mirror::Object> obj; if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { obj = AllocLargeObject<kInstrumented, PreFenceVisitor>(self, &klass, byte_count, pre_fence_visitor); if (obj != nullptr) { - return obj; + return obj.Ptr(); } else { // There should be an OOM exception, since we are retrying, clear it. self->ClearException(); @@ -85,7 +88,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, obj->SetClass(klass); if (kUseBakerOrBrooksReadBarrier) { if (kUseBrooksReadBarrier) { - obj->SetReadBarrierPointer(obj); + obj->SetReadBarrierPointer(obj.Ptr()); } obj->AssertReadBarrierPointer(); } @@ -93,14 +96,15 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, usable_size = bytes_allocated; pre_fence_visitor(obj, usable_size); QuasiAtomic::ThreadFenceForConstructor(); - } else if (!kInstrumented && allocator == kAllocatorTypeRosAlloc && - (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) && - LIKELY(obj != nullptr)) { + } else if ( + !kInstrumented && allocator == kAllocatorTypeRosAlloc && + (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) != nullptr && + LIKELY(obj != nullptr)) { DCHECK(!is_running_on_memory_tool_); obj->SetClass(klass); if (kUseBakerOrBrooksReadBarrier) { if (kUseBrooksReadBarrier) { - obj->SetReadBarrierPointer(obj); + obj->SetReadBarrierPointer(obj.Ptr()); } obj->AssertReadBarrierPointer(); } @@ -141,7 +145,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, obj->SetClass(klass); if (kUseBakerOrBrooksReadBarrier) { if (kUseBrooksReadBarrier) { - obj->SetReadBarrierPointer(obj); + obj->SetReadBarrierPointer(obj.Ptr()); } obj->AssertReadBarrierPointer(); } @@ -213,25 +217,25 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, } VerifyObject(obj); self->VerifyStack(); - return obj; + return obj.Ptr(); } // The size of a thread-local allocation stack in the number of references. static constexpr size_t kThreadLocalAllocationStackSize = 128; -inline void Heap::PushOnAllocationStack(Thread* self, mirror::Object** obj) { +inline void Heap::PushOnAllocationStack(Thread* self, ObjPtr<mirror::Object>* obj) { if (kUseThreadLocalAllocationStack) { - if (UNLIKELY(!self->PushOnThreadLocalAllocationStack(*obj))) { + if (UNLIKELY(!self->PushOnThreadLocalAllocationStack(obj->Ptr()))) { PushOnThreadLocalAllocationStackWithInternalGC(self, obj); } - } else if (UNLIKELY(!allocation_stack_->AtomicPushBack(*obj))) { + } else if (UNLIKELY(!allocation_stack_->AtomicPushBack(obj->Ptr()))) { PushOnAllocationStackWithInternalGC(self, obj); } } template <bool kInstrumented, typename PreFenceVisitor> inline mirror::Object* Heap::AllocLargeObject(Thread* self, - mirror::Class** klass, + ObjPtr<mirror::Class>* klass, size_t byte_count, const PreFenceVisitor& pre_fence_visitor) { // Save and restore the class in case it moves. @@ -405,7 +409,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, return ret; } -inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const { +inline bool Heap::ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const { // We need to have a zygote space or else our newly allocated large object can end up in the // Zygote resulting in it being prematurely freed. // We can only do this for primitive objects since large objects will not be within the card table @@ -435,7 +439,7 @@ inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, - mirror::Object** obj) { + ObjPtr<mirror::Object>* obj) { if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) { RequestConcurrentGCAndSaveObject(self, false, obj); } @@ -447,6 +451,16 @@ inline void Heap::WriteBarrierField(ObjPtr<mirror::Object> dst, card_table_->MarkCard(dst.Ptr()); } +inline void Heap::WriteBarrierArray(ObjPtr<mirror::Object> dst, + int start_offset ATTRIBUTE_UNUSED, + size_t length ATTRIBUTE_UNUSED) { + card_table_->MarkCard(dst.Ptr()); +} + +inline void Heap::WriteBarrierEveryFieldOf(ObjPtr<mirror::Object> obj) { + card_table_->MarkCard(obj.Ptr()); +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 33f849adf3..db90a2a616 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -47,6 +47,7 @@ #include "gc/collector/semi_space.h" #include "gc/collector/sticky_mark_sweep.h" #include "gc/reference_processor.h" +#include "gc/scoped_gc_critical_section.h" #include "gc/space/bump_pointer_space.h" #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/image_space.h" @@ -760,83 +761,6 @@ void Heap::DisableMovingGc() { } } -std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) { - if (!IsValidContinuousSpaceObjectAddress(klass)) { - return StringPrintf("<non heap address klass %p>", klass); - } - mirror::Class* component_type = klass->GetComponentType<kVerifyNone>(); - if (IsValidContinuousSpaceObjectAddress(component_type) && klass->IsArrayClass<kVerifyNone>()) { - std::string result("["); - result += SafeGetClassDescriptor(component_type); - return result; - } else if (UNLIKELY(klass->IsPrimitive<kVerifyNone>())) { - return Primitive::Descriptor(klass->GetPrimitiveType<kVerifyNone>()); - } else if (UNLIKELY(klass->IsProxyClass<kVerifyNone>())) { - return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(klass); - } else { - mirror::DexCache* dex_cache = klass->GetDexCache<kVerifyNone>(); - if (!IsValidContinuousSpaceObjectAddress(dex_cache)) { - return StringPrintf("<non heap address dex_cache %p>", dex_cache); - } - const DexFile* dex_file = dex_cache->GetDexFile(); - uint16_t class_def_idx = klass->GetDexClassDefIndex(); - if (class_def_idx == DexFile::kDexNoIndex16) { - return "<class def not found>"; - } - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); - const DexFile::TypeId& type_id = dex_file->GetTypeId(class_def.class_idx_); - return dex_file->GetTypeDescriptor(type_id); - } -} - -std::string Heap::SafePrettyTypeOf(mirror::Object* obj) { - if (obj == nullptr) { - return "null"; - } - mirror::Class* klass = obj->GetClass<kVerifyNone>(); - if (klass == nullptr) { - return "(class=null)"; - } - std::string result(SafeGetClassDescriptor(klass)); - if (obj->IsClass()) { - result += "<" + SafeGetClassDescriptor(obj->AsClass<kVerifyNone>()) + ">"; - } - return result; -} - -void Heap::DumpObject(std::ostream& stream, mirror::Object* obj) { - if (obj == nullptr) { - stream << "(obj=null)"; - return; - } - if (IsAligned<kObjectAlignment>(obj)) { - space::Space* space = nullptr; - // Don't use find space since it only finds spaces which actually contain objects instead of - // spaces which may contain objects (e.g. cleared bump pointer spaces). - for (const auto& cur_space : continuous_spaces_) { - if (cur_space->HasAddress(obj)) { - space = cur_space; - break; - } - } - // Unprotect all the spaces. - for (const auto& con_space : continuous_spaces_) { - mprotect(con_space->Begin(), con_space->Capacity(), PROT_READ | PROT_WRITE); - } - stream << "Object " << obj; - if (space != nullptr) { - stream << " in space " << *space; - } - mirror::Class* klass = obj->GetClass<kVerifyNone>(); - stream << "\nclass=" << klass; - if (klass != nullptr) { - stream << " type= " << SafePrettyTypeOf(obj); - } - // Re-protect the address we faulted on. - mprotect(AlignDown(obj, kPageSize), kPageSize, PROT_NONE); - } -} - bool Heap::IsCompilingBoot() const { if (!Runtime::Current()->IsAotCompiler()) { return false; @@ -1012,10 +936,14 @@ void Heap::VisitObjects(ObjectCallback callback, void* arg) { } DecrementDisableMovingGC(self); } else { + // Since concurrent moving GC has thread suspension, also poison ObjPtr the normal case to + // catch bugs. + self->PoisonObjectPointers(); // GCs can move objects, so don't allow this. ScopedAssertNoThreadSuspension ants("Visiting objects"); DCHECK(region_space_ == nullptr); VisitObjectsInternal(callback, arg); + self->PoisonObjectPointers(); } } @@ -1325,33 +1253,42 @@ Heap::~Heap() { VLOG(heap) << "Finished ~Heap()"; } -space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(const mirror::Object* obj, - bool fail_ok) const { + +space::ContinuousSpace* Heap::FindContinuousSpaceFromAddress(const mirror::Object* addr) const { for (const auto& space : continuous_spaces_) { - if (space->Contains(obj)) { + if (space->Contains(addr)) { return space; } } + return nullptr; +} + +space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(ObjPtr<mirror::Object> obj, + bool fail_ok) const { + space::ContinuousSpace* space = FindContinuousSpaceFromAddress(obj.Ptr()); + if (space != nullptr) { + return space; + } if (!fail_ok) { - LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!"; + LOG(FATAL) << "object " << obj << " not inside any spaces!"; } return nullptr; } -space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(const mirror::Object* obj, +space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const { for (const auto& space : discontinuous_spaces_) { - if (space->Contains(obj)) { + if (space->Contains(obj.Ptr())) { return space; } } if (!fail_ok) { - LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!"; + LOG(FATAL) << "object " << obj << " not inside any spaces!"; } return nullptr; } -space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) const { +space::Space* Heap::FindSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const { space::Space* result = FindContinuousSpaceFromObject(obj, true); if (result != nullptr) { return result; @@ -1359,6 +1296,21 @@ space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) return FindDiscontinuousSpaceFromObject(obj, fail_ok); } +space::Space* Heap::FindSpaceFromAddress(const void* addr) const { + for (const auto& space : continuous_spaces_) { + if (space->Contains(reinterpret_cast<const mirror::Object*>(addr))) { + return space; + } + } + for (const auto& space : discontinuous_spaces_) { + if (space->Contains(reinterpret_cast<const mirror::Object*>(addr))) { + return space; + } + } + return nullptr; +} + + void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) { // If we're in a stack overflow, do not create a new exception. It would require running the // constructor, which will of course still be in a stack overflow. @@ -1423,6 +1375,8 @@ void Heap::Trim(Thread* self) { // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care // about pauses. ScopedTrace trace("Deflating monitors"); + // Avoid race conditions on the lock word for CC. + ScopedGCCriticalSection gcs(self, kGcCauseTrim, kCollectorTypeHeapTrim); ScopedSuspendAll ssa(__FUNCTION__); uint64_t start_time = NanoTime(); size_t count = runtime->GetMonitorList()->DeflateMonitors(); @@ -1523,62 +1477,49 @@ void Heap::TrimSpaces(Thread* self) { << static_cast<int>(100 * managed_utilization) << "%."; } -bool Heap::IsValidObjectAddress(ObjPtr<mirror::Object> obj) const { - // Note: we deliberately don't take the lock here, and mustn't test anything that would require - // taking the lock. - if (obj == nullptr) { +bool Heap::IsValidObjectAddress(const void* addr) const { + if (addr == nullptr) { return true; } - return IsAligned<kObjectAlignment>(obj.Ptr()) && - FindSpaceFromObject(obj.Ptr(), true) != nullptr; + return IsAligned<kObjectAlignment>(addr) && FindSpaceFromAddress(addr) != nullptr; } -bool Heap::IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const { - return FindContinuousSpaceFromObject(obj, true) != nullptr; +bool Heap::IsNonDiscontinuousSpaceHeapAddress(const void* addr) const { + return FindContinuousSpaceFromAddress(reinterpret_cast<const mirror::Object*>(addr)) != nullptr; } -bool Heap::IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const { - if (obj == nullptr || !IsAligned<kObjectAlignment>(obj)) { +bool Heap::IsLiveObjectLocked(ObjPtr<mirror::Object> obj, + bool search_allocation_stack, + bool search_live_stack, + bool sorted) { + if (UNLIKELY(!IsAligned<kObjectAlignment>(obj.Ptr()))) { return false; } - for (const auto& space : continuous_spaces_) { - if (space->HasAddress(obj)) { - return true; - } - } - return false; -} - -bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack, - bool search_live_stack, bool sorted) { - if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { - return false; - } - if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj)) { + if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj.Ptr())) { mirror::Class* klass = obj->GetClass<kVerifyNone>(); if (obj == klass) { // This case happens for java.lang.Class. return true; } return VerifyClassClass(klass) && IsLiveObjectLocked(klass); - } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj)) { + } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj.Ptr())) { // If we are in the allocated region of the temp space, then we are probably live (e.g. during // a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained. - return temp_space_->Contains(obj); + return temp_space_->Contains(obj.Ptr()); } - if (region_space_ != nullptr && region_space_->HasAddress(obj)) { + if (region_space_ != nullptr && region_space_->HasAddress(obj.Ptr())) { return true; } space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true); space::DiscontinuousSpace* d_space = nullptr; if (c_space != nullptr) { - if (c_space->GetLiveBitmap()->Test(obj)) { + if (c_space->GetLiveBitmap()->Test(obj.Ptr())) { return true; } } else { d_space = FindDiscontinuousSpaceFromObject(obj, true); if (d_space != nullptr) { - if (d_space->GetLiveBitmap()->Test(obj)) { + if (d_space->GetLiveBitmap()->Test(obj.Ptr())) { return true; } } @@ -1590,20 +1531,20 @@ bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack, } if (search_allocation_stack) { if (sorted) { - if (allocation_stack_->ContainsSorted(obj)) { + if (allocation_stack_->ContainsSorted(obj.Ptr())) { return true; } - } else if (allocation_stack_->Contains(obj)) { + } else if (allocation_stack_->Contains(obj.Ptr())) { return true; } } if (search_live_stack) { if (sorted) { - if (live_stack_->ContainsSorted(obj)) { + if (live_stack_->ContainsSorted(obj.Ptr())) { return true; } - } else if (live_stack_->Contains(obj)) { + } else if (live_stack_->Contains(obj.Ptr())) { return true; } } @@ -1611,12 +1552,12 @@ bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack, // We need to check the bitmaps again since there is a race where we mark something as live and // then clear the stack containing it. if (c_space != nullptr) { - if (c_space->GetLiveBitmap()->Test(obj)) { + if (c_space->GetLiveBitmap()->Test(obj.Ptr())) { return true; } } else { d_space = FindDiscontinuousSpaceFromObject(obj, true); - if (d_space != nullptr && d_space->GetLiveBitmap()->Test(obj)) { + if (d_space != nullptr && d_space->GetLiveBitmap()->Test(obj.Ptr())) { return true; } } @@ -1646,7 +1587,7 @@ void Heap::DumpSpaces(std::ostream& stream) const { } } -void Heap::VerifyObjectBody(mirror::Object* obj) { +void Heap::VerifyObjectBody(ObjPtr<mirror::Object> obj) { if (verify_object_mode_ == kVerifyObjectModeDisabled) { return; } @@ -1655,7 +1596,7 @@ void Heap::VerifyObjectBody(mirror::Object* obj) { if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_.LoadRelaxed()) < 10 * KB)) { return; } - CHECK_ALIGNED(obj, kObjectAlignment) << "Object isn't aligned"; + CHECK_ALIGNED(obj.Ptr(), kObjectAlignment) << "Object isn't aligned"; mirror::Class* c = obj->GetFieldObject<mirror::Class, kVerifyNone>(mirror::Object::ClassOffset()); CHECK(c != nullptr) << "Null class in object " << obj; CHECK_ALIGNED(c, kObjectAlignment) << "Class " << c << " not aligned in object " << obj; @@ -1734,14 +1675,13 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated, - mirror::Class** klass) { + ObjPtr<mirror::Class>* klass) { bool was_default_allocator = allocator == GetCurrentAllocator(); // Make sure there is no pending exception since we may need to throw an OOME. self->AssertNoPendingException(); DCHECK(klass != nullptr); StackHandleScope<1> hs(self); - HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(klass)); - klass = nullptr; // Invalidate for safety. + HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(klass)); // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. collector::GcType last_gc = WaitForGcToComplete(kGcCauseForAlloc, self); @@ -1944,7 +1884,7 @@ uint64_t Heap::GetBytesAllocatedEver() const { class InstanceCounter { public: - InstanceCounter(const std::vector<mirror::Class*>& classes, + InstanceCounter(const std::vector<Handle<mirror::Class>>& classes, bool use_is_assignable_from, uint64_t* counts) REQUIRES_SHARED(Locks::mutator_lock_) @@ -1956,7 +1896,7 @@ class InstanceCounter { mirror::Class* instance_class = obj->GetClass(); CHECK(instance_class != nullptr); for (size_t i = 0; i < instance_counter->classes_.size(); ++i) { - mirror::Class* klass = instance_counter->classes_[i]; + ObjPtr<mirror::Class> klass = instance_counter->classes_[i].Get(); if (instance_counter->use_is_assignable_from_) { if (klass != nullptr && klass->IsAssignableFrom(instance_class)) { ++instance_counter->counts_[i]; @@ -1968,13 +1908,14 @@ class InstanceCounter { } private: - const std::vector<mirror::Class*>& classes_; + const std::vector<Handle<mirror::Class>>& classes_; bool use_is_assignable_from_; uint64_t* const counts_; DISALLOW_COPY_AND_ASSIGN(InstanceCounter); }; -void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from, +void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes, + bool use_is_assignable_from, uint64_t* counts) { InstanceCounter counter(classes, use_is_assignable_from, counts); VisitObjects(InstanceCounter::Callback, &counter); @@ -1982,44 +1923,55 @@ void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_i class InstanceCollector { public: - InstanceCollector(mirror::Class* c, int32_t max_count, std::vector<mirror::Object*>& instances) + InstanceCollector(VariableSizedHandleScope& scope, + Handle<mirror::Class> c, + int32_t max_count, + std::vector<Handle<mirror::Object>>& instances) REQUIRES_SHARED(Locks::mutator_lock_) - : class_(c), max_count_(max_count), instances_(instances) { - } + : scope_(scope), + class_(c), + max_count_(max_count), + instances_(instances) {} + static void Callback(mirror::Object* obj, void* arg) REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { DCHECK(arg != nullptr); InstanceCollector* instance_collector = reinterpret_cast<InstanceCollector*>(arg); - if (obj->GetClass() == instance_collector->class_) { + if (obj->GetClass() == instance_collector->class_.Get()) { if (instance_collector->max_count_ == 0 || instance_collector->instances_.size() < instance_collector->max_count_) { - instance_collector->instances_.push_back(obj); + instance_collector->instances_.push_back(instance_collector->scope_.NewHandle(obj)); } } } private: - const mirror::Class* const class_; + VariableSizedHandleScope& scope_; + Handle<mirror::Class> const class_; const uint32_t max_count_; - std::vector<mirror::Object*>& instances_; + std::vector<Handle<mirror::Object>>& instances_; DISALLOW_COPY_AND_ASSIGN(InstanceCollector); }; -void Heap::GetInstances(mirror::Class* c, +void Heap::GetInstances(VariableSizedHandleScope& scope, + Handle<mirror::Class> c, int32_t max_count, - std::vector<mirror::Object*>& instances) { - InstanceCollector collector(c, max_count, instances); + std::vector<Handle<mirror::Object>>& instances) { + InstanceCollector collector(scope, c, max_count, instances); VisitObjects(&InstanceCollector::Callback, &collector); } class ReferringObjectsFinder { public: - ReferringObjectsFinder(mirror::Object* object, + ReferringObjectsFinder(VariableSizedHandleScope& scope, + Handle<mirror::Object> object, int32_t max_count, - std::vector<mirror::Object*>& referring_objects) + std::vector<Handle<mirror::Object>>& referring_objects) REQUIRES_SHARED(Locks::mutator_lock_) - : object_(object), max_count_(max_count), referring_objects_(referring_objects) { - } + : scope_(scope), + object_(object), + max_count_(max_count), + referring_objects_(referring_objects) {} static void Callback(mirror::Object* obj, void* arg) REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { @@ -2029,16 +1981,18 @@ class ReferringObjectsFinder { // For bitmap Visit. // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for // annotalysis on visitors. - void operator()(mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS { + void operator()(ObjPtr<mirror::Object> o) const NO_THREAD_SAFETY_ANALYSIS { o->VisitReferences(*this, VoidFunctor()); } // For Object::VisitReferences. - void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<mirror::Object> obj, + MemberOffset offset, + bool is_static ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset); - if (ref == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) { - referring_objects_.push_back(obj); + if (ref == object_.Get() && (max_count_ == 0 || referring_objects_.size() < max_count_)) { + referring_objects_.push_back(scope_.NewHandle(obj)); } } @@ -2047,15 +2001,18 @@ class ReferringObjectsFinder { void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} private: - const mirror::Object* const object_; + VariableSizedHandleScope& scope_; + Handle<mirror::Object> const object_; const uint32_t max_count_; - std::vector<mirror::Object*>& referring_objects_; + std::vector<Handle<mirror::Object>>& referring_objects_; DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder); }; -void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count, - std::vector<mirror::Object*>& referring_objects) { - ReferringObjectsFinder finder(o, max_count, referring_objects); +void Heap::GetReferringObjects(VariableSizedHandleScope& scope, + Handle<mirror::Object> o, + int32_t max_count, + std::vector<Handle<mirror::Object>>& referring_objects) { + ReferringObjectsFinder finder(scope, o, max_count, referring_objects); VisitObjects(&ReferringObjectsFinder::Callback, &finder); } @@ -3113,41 +3070,42 @@ class VerifyObjectVisitor { const bool verify_referent_; }; -void Heap::PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) { +void Heap::PushOnAllocationStackWithInternalGC(Thread* self, ObjPtr<mirror::Object>* obj) { // Slow path, the allocation stack push back must have already failed. - DCHECK(!allocation_stack_->AtomicPushBack(*obj)); + DCHECK(!allocation_stack_->AtomicPushBack(obj->Ptr())); do { // TODO: Add handle VerifyObject. StackHandleScope<1> hs(self); - HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj)); + HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj)); // Push our object into the reserve region of the allocaiton stack. This is only required due // to heap verification requiring that roots are live (either in the live bitmap or in the // allocation stack). - CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(*obj)); + CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(obj->Ptr())); CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); - } while (!allocation_stack_->AtomicPushBack(*obj)); + } while (!allocation_stack_->AtomicPushBack(obj->Ptr())); } -void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) { +void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self, + ObjPtr<mirror::Object>* obj) { // Slow path, the allocation stack push back must have already failed. - DCHECK(!self->PushOnThreadLocalAllocationStack(*obj)); + DCHECK(!self->PushOnThreadLocalAllocationStack(obj->Ptr())); StackReference<mirror::Object>* start_address; StackReference<mirror::Object>* end_address; while (!allocation_stack_->AtomicBumpBack(kThreadLocalAllocationStackSize, &start_address, &end_address)) { // TODO: Add handle VerifyObject. StackHandleScope<1> hs(self); - HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj)); + HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj)); // Push our object into the reserve region of the allocaiton stack. This is only required due // to heap verification requiring that roots are live (either in the live bitmap or in the // allocation stack). - CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(*obj)); + CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(obj->Ptr())); // Push into the reserve allocation stack. CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); } self->SetThreadLocalAllocationStack(start_address, end_address); // Retry on the new thread-local allocation stack. - CHECK(self->PushOnThreadLocalAllocationStack(*obj)); // Must succeed. + CHECK(self->PushOnThreadLocalAllocationStack(obj->Ptr())); // Must succeed. } // Must do this with mutators suspended since we are directly accessing the allocation stacks. @@ -3737,7 +3695,7 @@ void Heap::ClearGrowthLimit() { } } -void Heap::AddFinalizerReference(Thread* self, mirror::Object** object) { +void Heap::AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object) { ScopedObjectAccess soa(self); ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(*object)); jvalue args[1]; @@ -3747,9 +3705,11 @@ void Heap::AddFinalizerReference(Thread* self, mirror::Object** object) { *object = soa.Decode<mirror::Object>(arg.get()).Ptr(); } -void Heap::RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj) { +void Heap::RequestConcurrentGCAndSaveObject(Thread* self, + bool force_full, + ObjPtr<mirror::Object>* obj) { StackHandleScope<1> hs(self); - HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj)); + HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj)); RequestConcurrentGC(self, force_full); } @@ -4026,7 +3986,7 @@ void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) { mod_union_tables_.Put(mod_union_table->GetSpace(), mod_union_table); } -void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { +void Heap::CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count) { CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || (c->IsVariableSize() || c->GetObjectSize() == byte_count)) << c->GetClassFlags(); CHECK_GE(byte_count, sizeof(mirror::Object)); @@ -4152,7 +4112,7 @@ static size_t get_backtrace(uintptr_t* frames, size_t max_depth) { return state.GetFrameCount(); } -void Heap::CheckGcStressMode(Thread* self, mirror::Object** obj) { +void Heap::CheckGcStressMode(Thread* self, ObjPtr<mirror::Object>* obj) { auto* const runtime = Runtime::Current(); if (gc_stress_mode_ && runtime->GetClassLinker()->IsInitialized() && !runtime->IsActiveTransaction() && mirror::Class::HasJavaLangClass()) { @@ -4191,9 +4151,9 @@ void Heap::DisableGCForShutdown() { gc_disabled_for_shutdown_ = true; } -bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const { +bool Heap::ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const { for (gc::space::ImageSpace* space : boot_image_spaces_) { - if (space->HasAddress(obj)) { + if (space->HasAddress(obj.Ptr())) { return true; } } @@ -4250,7 +4210,7 @@ void Heap::RemoveAllocationListener() { AllocationListener* old = GetAndOverwriteAllocationListener(&alloc_listener_, nullptr); if (old != nullptr) { - Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints(); } } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 5e17a52429..6d37140e81 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -34,6 +34,7 @@ #include "gc/collector_type.h" #include "gc/space/large_object_space.h" #include "globals.h" +#include "handle.h" #include "obj_ptr.h" #include "object_callbacks.h" #include "offsets.h" @@ -49,6 +50,7 @@ class StackVisitor; class Thread; class ThreadPool; class TimingLogger; +class VariableSizedHandleScope; namespace mirror { class Class; @@ -194,36 +196,48 @@ class Heap { // Allocates and initializes storage for an object instance. template <bool kInstrumented, typename PreFenceVisitor> mirror::Object* AllocObject(Thread* self, - mirror::Class* klass, + ObjPtr<mirror::Class> klass, size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, + REQUIRES(!*gc_complete_lock_, + !*pending_task_lock_, + !*backtrace_lock_, !Roles::uninterruptible_) { - return AllocObjectWithAllocator<kInstrumented, true>( - self, klass, num_bytes, GetCurrentAllocator(), pre_fence_visitor); + return AllocObjectWithAllocator<kInstrumented, true>(self, + klass, + num_bytes, + GetCurrentAllocator(), + pre_fence_visitor); } template <bool kInstrumented, typename PreFenceVisitor> mirror::Object* AllocNonMovableObject(Thread* self, - mirror::Class* klass, + ObjPtr<mirror::Class> klass, size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, + REQUIRES(!*gc_complete_lock_, + !*pending_task_lock_, + !*backtrace_lock_, !Roles::uninterruptible_) { - return AllocObjectWithAllocator<kInstrumented, true>( - self, klass, num_bytes, GetCurrentNonMovingAllocator(), pre_fence_visitor); + return AllocObjectWithAllocator<kInstrumented, true>(self, + klass, + num_bytes, + GetCurrentNonMovingAllocator(), + pre_fence_visitor); } template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor> ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(Thread* self, - mirror::Class* klass, + ObjPtr<mirror::Class> klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, + REQUIRES(!*gc_complete_lock_, + !*pending_task_lock_, + !*backtrace_lock_, !Roles::uninterruptible_); AllocatorType GetCurrentAllocator() const { @@ -241,7 +255,7 @@ class Heap { void VisitObjectsPaused(ObjectCallback callback, void* arg) REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_); - void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) + void CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count) REQUIRES_SHARED(Locks::mutator_lock_); void RegisterNativeAllocation(JNIEnv* env, size_t bytes) @@ -263,7 +277,7 @@ class Heap { // The given reference is believed to be to an object in the Java heap, check the soundness of it. // TODO: NO_THREAD_SAFETY_ANALYSIS since we call this everywhere and it is impossible to find a // proper lock ordering for it. - void VerifyObjectBody(mirror::Object* o) NO_THREAD_SAFETY_ANALYSIS; + void VerifyObjectBody(ObjPtr<mirror::Object> o) NO_THREAD_SAFETY_ANALYSIS; // Check sanity of all live references. void VerifyHeap() REQUIRES(!Locks::heap_bitmap_lock_); @@ -276,16 +290,16 @@ class Heap { // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock, // and doesn't abort on error, allowing the caller to report more // meaningful diagnostics. - bool IsValidObjectAddress(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_); + bool IsValidObjectAddress(const void* obj) const REQUIRES_SHARED(Locks::mutator_lock_); // Faster alternative to IsHeapAddress since finding if an object is in the large object space is // very slow. - bool IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const + bool IsNonDiscontinuousSpaceHeapAddress(const void* addr) const REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if 'obj' is a live heap object, false otherwise (including for invalid addresses). // Requires the heap lock to be held. - bool IsLiveObjectLocked(mirror::Object* obj, + bool IsLiveObjectLocked(ObjPtr<mirror::Object> obj, bool search_allocation_stack = true, bool search_live_stack = true, bool sorted = false) @@ -321,19 +335,25 @@ class Heap { // Implements VMDebug.countInstancesOfClass and JDWP VM_InstanceCount. // The boolean decides whether to use IsAssignableFrom or == when comparing classes. - void CountInstances(const std::vector<mirror::Class*>& classes, + void CountInstances(const std::vector<Handle<mirror::Class>>& classes, bool use_is_assignable_from, uint64_t* counts) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Implements JDWP RT_Instances. - void GetInstances(mirror::Class* c, int32_t max_count, std::vector<mirror::Object*>& instances) + void GetInstances(VariableSizedHandleScope& scope, + Handle<mirror::Class> c, + int32_t max_count, + std::vector<Handle<mirror::Object>>& instances) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // Implements JDWP OR_ReferringObjects. - void GetReferringObjects(mirror::Object* o, + void GetReferringObjects(VariableSizedHandleScope& scope, + Handle<mirror::Object> o, int32_t max_count, - std::vector<mirror::Object*>& referring_objects) + std::vector<Handle<mirror::Object>>& referring_objects) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -445,16 +465,14 @@ class Heap { REQUIRES_SHARED(Locks::mutator_lock_); // Write barrier for array operations that update many field positions - ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst, - int start_offset ATTRIBUTE_UNUSED, + ALWAYS_INLINE void WriteBarrierArray(ObjPtr<mirror::Object> dst, + int start_offset, // TODO: element_count or byte_count? - size_t length ATTRIBUTE_UNUSED) { - card_table_->MarkCard(dst); - } + size_t length) + REQUIRES_SHARED(Locks::mutator_lock_); - ALWAYS_INLINE void WriteBarrierEveryFieldOf(const mirror::Object* obj) { - card_table_->MarkCard(obj); - } + ALWAYS_INLINE void WriteBarrierEveryFieldOf(ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_); accounting::CardTable* GetCardTable() const { return card_table_.get(); @@ -464,7 +482,7 @@ class Heap { return rb_table_.get(); } - void AddFinalizerReference(Thread* self, mirror::Object** object); + void AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object); // Returns the number of bytes currently allocated. size_t GetBytesAllocated() const { @@ -527,12 +545,20 @@ class Heap { // get the space that corresponds to an object's address. Current implementation searches all // spaces in turn. If fail_ok is false then failing to find a space will cause an abort. // TODO: consider using faster data structure like binary tree. - space::ContinuousSpace* FindContinuousSpaceFromObject(const mirror::Object*, bool fail_ok) const + space::ContinuousSpace* FindContinuousSpaceFromObject(ObjPtr<mirror::Object>, bool fail_ok) const + REQUIRES_SHARED(Locks::mutator_lock_); + + space::ContinuousSpace* FindContinuousSpaceFromAddress(const mirror::Object* addr) const REQUIRES_SHARED(Locks::mutator_lock_); - space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(const mirror::Object*, + + space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(ObjPtr<mirror::Object>, bool fail_ok) const REQUIRES_SHARED(Locks::mutator_lock_); - space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const + + space::Space* FindSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const + REQUIRES_SHARED(Locks::mutator_lock_); + + space::Space* FindSpaceFromAddress(const void* ptr) const REQUIRES_SHARED(Locks::mutator_lock_); void DumpForSigQuit(std::ostream& os) REQUIRES(!*gc_complete_lock_, !native_histogram_lock_); @@ -598,7 +624,7 @@ class Heap { return boot_image_spaces_; } - bool ObjectIsInBootImageSpace(mirror::Object* obj) const + bool ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_); bool IsInBootImageOatFile(const void* p) const @@ -650,12 +676,6 @@ class Heap { void DumpSpaces(std::ostream& stream) const REQUIRES_SHARED(Locks::mutator_lock_); std::string DumpSpaces() const REQUIRES_SHARED(Locks::mutator_lock_); - // Dump object should only be used by the signal handler. - void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; - // Safe version of pretty type of which check to make sure objects are heap addresses. - std::string SafeGetClassDescriptor(mirror::Class* klass) NO_THREAD_SAFETY_ANALYSIS; - std::string SafePrettyTypeOf(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; - // GC performance measuring void DumpGcPerformanceInfo(std::ostream& os) REQUIRES(!*gc_complete_lock_, !native_histogram_lock_); @@ -837,11 +857,11 @@ class Heap { collector_type == kCollectorTypeMC || collector_type == kCollectorTypeHomogeneousSpaceCompact; } - bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const + bool ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, - mirror::Object** obj) + ObjPtr<mirror::Object>* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*pending_task_lock_, !*gc_complete_lock_); @@ -852,7 +872,7 @@ class Heap { // We don't force this to be inlined since it is a slow path. template <bool kInstrumented, typename PreFenceVisitor> mirror::Object* AllocLargeObject(Thread* self, - mirror::Class** klass, + ObjPtr<mirror::Class>* klass, size_t byte_count, const PreFenceVisitor& pre_fence_visitor) REQUIRES_SHARED(Locks::mutator_lock_) @@ -867,14 +887,14 @@ class Heap { size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated, - mirror::Class** klass) + ObjPtr<mirror::Class>* klass) REQUIRES(!Locks::thread_suspend_count_lock_, !*gc_complete_lock_, !*pending_task_lock_) REQUIRES_SHARED(Locks::mutator_lock_); // Allocate into a specific space. mirror::Object* AllocateInto(Thread* self, space::AllocSpace* space, - mirror::Class* c, + ObjPtr<mirror::Class> c, size_t bytes) REQUIRES_SHARED(Locks::mutator_lock_); @@ -899,10 +919,6 @@ class Heap { template <bool kGrow> ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size); - // Returns true if the address passed in is within the address range of a continuous space. - bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const - REQUIRES_SHARED(Locks::mutator_lock_); - // Run the finalizers. If timeout is non zero, then we use the VMRuntime version. void RunFinalization(JNIEnv* env, uint64_t timeout); @@ -914,7 +930,7 @@ class Heap { void RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) REQUIRES(!*pending_task_lock_); - void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj) + void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, ObjPtr<mirror::Object>* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*pending_task_lock_); bool IsGCRequestPending() const; @@ -986,13 +1002,13 @@ class Heap { REQUIRES_SHARED(Locks::mutator_lock_); // Push an object onto the allocation stack. - void PushOnAllocationStack(Thread* self, mirror::Object** obj) + void PushOnAllocationStack(Thread* self, ObjPtr<mirror::Object>* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); - void PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) + void PushOnAllocationStackWithInternalGC(Thread* self, ObjPtr<mirror::Object>* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); - void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, mirror::Object** obj) + void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, ObjPtr<mirror::Object>* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); @@ -1023,7 +1039,7 @@ class Heap { void UpdateGcCountRateHistograms() REQUIRES(gc_complete_lock_); // GC stress mode attempts to do one GC per unique backtrace. - void CheckGcStressMode(Thread* self, mirror::Object** obj) + void CheckGcStressMode(Thread* self, ObjPtr<mirror::Object>* obj) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_); diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h index cceb0072a9..b212d095cb 100644 --- a/runtime/handle_scope-inl.h +++ b/runtime/handle_scope-inl.h @@ -28,24 +28,30 @@ namespace art { template<size_t kNumReferences> -inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value) - : HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) { - DCHECK_EQ(self, Thread::Current()); +inline FixedSizeHandleScope<kNumReferences>::FixedSizeHandleScope(BaseHandleScope* link, + mirror::Object* fill_value) + : HandleScope(link, kNumReferences) { if (kDebugLocking) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } - static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference"); - // TODO: Figure out how to use a compile assert. - CHECK_EQ(&storage_[0], GetReferences()); + static_assert(kNumReferences >= 1, "FixedSizeHandleScope must contain at least 1 reference"); + DCHECK_EQ(&storage_[0], GetReferences()); // TODO: Figure out how to use a compile assert. for (size_t i = 0; i < kNumReferences; ++i) { SetReference(i, fill_value); } +} + +template<size_t kNumReferences> +inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value) + : FixedSizeHandleScope<kNumReferences>(self->GetTopHandleScope(), fill_value), + self_(self) { + DCHECK_EQ(self, Thread::Current()); self_->PushHandleScope(this); } template<size_t kNumReferences> inline StackHandleScope<kNumReferences>::~StackHandleScope() { - HandleScope* top_handle_scope = self_->PopHandleScope(); + BaseHandleScope* top_handle_scope = self_->PopHandleScope(); DCHECK_EQ(top_handle_scope, this); if (kDebugLocking) { Locks::mutator_lock_->AssertSharedHeld(self_); @@ -66,7 +72,7 @@ inline size_t HandleScope::SizeOf(PointerSize pointer_size, uint32_t num_referen } inline mirror::Object* HandleScope::GetReference(size_t i) const { - DCHECK_LT(i, number_of_references_); + DCHECK_LT(i, NumberOfReferences()); if (kDebugLocking) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } @@ -74,12 +80,12 @@ inline mirror::Object* HandleScope::GetReference(size_t i) const { } inline Handle<mirror::Object> HandleScope::GetHandle(size_t i) { - DCHECK_LT(i, number_of_references_); + DCHECK_LT(i, NumberOfReferences()); return Handle<mirror::Object>(&GetReferences()[i]); } inline MutableHandle<mirror::Object> HandleScope::GetMutableHandle(size_t i) { - DCHECK_LT(i, number_of_references_); + DCHECK_LT(i, NumberOfReferences()); return MutableHandle<mirror::Object>(&GetReferences()[i]); } @@ -87,7 +93,7 @@ inline void HandleScope::SetReference(size_t i, mirror::Object* object) { if (kDebugLocking) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } - DCHECK_LT(i, number_of_references_); + DCHECK_LT(i, NumberOfReferences()); GetReferences()[i].Assign(object); } @@ -95,13 +101,13 @@ inline bool HandleScope::Contains(StackReference<mirror::Object>* handle_scope_e // A HandleScope should always contain something. One created by the // jni_compiler should have a jobject/jclass as a native method is // passed in a this pointer or a class - DCHECK_GT(number_of_references_, 0U); + DCHECK_GT(NumberOfReferences(), 0U); return &GetReferences()[0] <= handle_scope_entry && handle_scope_entry <= &GetReferences()[number_of_references_ - 1]; } template<size_t kNumReferences> template<class T> -inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) { +inline MutableHandle<T> FixedSizeHandleScope<kNumReferences>::NewHandle(T* object) { SetReference(pos_, object); MutableHandle<T> h(GetHandle<T>(pos_)); pos_++; @@ -109,24 +115,24 @@ inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) { } template<size_t kNumReferences> template<class MirrorType, bool kPoison> -inline MutableHandle<MirrorType> StackHandleScope<kNumReferences>::NewHandle( +inline MutableHandle<MirrorType> FixedSizeHandleScope<kNumReferences>::NewHandle( ObjPtr<MirrorType, kPoison> object) { return NewHandle(object.Ptr()); } template<size_t kNumReferences> template<class T> -inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) { +inline HandleWrapper<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(T** object) { return HandleWrapper<T>(object, NewHandle(*object)); } template<size_t kNumReferences> template<class T> -inline HandleWrapperObjPtr<T> StackHandleScope<kNumReferences>::NewHandleWrapper( +inline HandleWrapperObjPtr<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper( ObjPtr<T>* object) { return HandleWrapperObjPtr<T>(object, NewHandle(*object)); } template<size_t kNumReferences> -inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) { +inline void FixedSizeHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) { if (kDebugLocking) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } @@ -135,12 +141,111 @@ inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Obj GetReferences()[i].Assign(object); } +// Number of references contained within this handle scope. +inline uint32_t BaseHandleScope::NumberOfReferences() const { + return LIKELY(!IsVariableSized()) + ? AsHandleScope()->NumberOfReferences() + : AsVariableSized()->NumberOfReferences(); +} + +inline bool BaseHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) const { + return LIKELY(!IsVariableSized()) + ? AsHandleScope()->Contains(handle_scope_entry) + : AsVariableSized()->Contains(handle_scope_entry); +} + +template <typename Visitor> +inline void BaseHandleScope::VisitRoots(Visitor& visitor) { + if (LIKELY(!IsVariableSized())) { + AsHandleScope()->VisitRoots(visitor); + } else { + AsVariableSized()->VisitRoots(visitor); + } +} + +inline VariableSizedHandleScope* BaseHandleScope::AsVariableSized() { + DCHECK(IsVariableSized()); + return down_cast<VariableSizedHandleScope*>(this); +} + +inline HandleScope* BaseHandleScope::AsHandleScope() { + DCHECK(!IsVariableSized()); + return down_cast<HandleScope*>(this); +} + +inline const VariableSizedHandleScope* BaseHandleScope::AsVariableSized() const { + DCHECK(IsVariableSized()); + return down_cast<const VariableSizedHandleScope*>(this); +} + +inline const HandleScope* BaseHandleScope::AsHandleScope() const { + DCHECK(!IsVariableSized()); + return down_cast<const HandleScope*>(this); +} + +template<class T> +MutableHandle<T> VariableSizedHandleScope::NewHandle(T* object) { + if (current_scope_->RemainingSlots() == 0) { + current_scope_ = new LocalScopeType(current_scope_); + } + return current_scope_->NewHandle(object); +} + template<class MirrorType, bool kPoison> -inline MutableHandle<MirrorType> StackHandleScopeCollection::NewHandle( +inline MutableHandle<MirrorType> VariableSizedHandleScope::NewHandle( ObjPtr<MirrorType, kPoison> ptr) { return NewHandle(ptr.Ptr()); } +inline VariableSizedHandleScope::VariableSizedHandleScope(Thread* const self) + : BaseHandleScope(self->GetTopHandleScope()), + self_(self) { + current_scope_ = new LocalScopeType(/*link*/ nullptr); + self_->PushHandleScope(this); +} + +inline VariableSizedHandleScope::~VariableSizedHandleScope() { + BaseHandleScope* top_handle_scope = self_->PopHandleScope(); + DCHECK_EQ(top_handle_scope, this); + while (current_scope_ != nullptr) { + LocalScopeType* next = reinterpret_cast<LocalScopeType*>(current_scope_->GetLink()); + delete current_scope_; + current_scope_ = next; + } +} + +inline uint32_t VariableSizedHandleScope::NumberOfReferences() const { + uint32_t sum = 0; + const LocalScopeType* cur = current_scope_; + while (cur != nullptr) { + sum += cur->NumberOfReferences(); + cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink()); + } + return sum; +} + +inline bool VariableSizedHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) + const { + const LocalScopeType* cur = current_scope_; + while (cur != nullptr) { + if (cur->Contains(handle_scope_entry)) { + return true; + } + cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink()); + } + return false; +} + +template <typename Visitor> +inline void VariableSizedHandleScope::VisitRoots(Visitor& visitor) { + LocalScopeType* cur = current_scope_; + while (cur != nullptr) { + cur->VisitRoots(visitor); + cur = reinterpret_cast<LocalScopeType*>(cur->GetLink()); + } +} + + } // namespace art #endif // ART_RUNTIME_HANDLE_SCOPE_INL_H_ diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index fc729a547b..8a0aba6121 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -29,26 +29,69 @@ namespace art { +class HandleScope; template<class MirrorType, bool kPoison> class ObjPtr; +class Thread; +class VariableSizedHandleScope; namespace mirror { class Object; } -class Thread; +// Basic handle scope, tracked by a list. May be variable sized. +class PACKED(4) BaseHandleScope { + public: + bool IsVariableSized() const { + return number_of_references_ == kNumReferencesVariableSized; + } + + // Number of references contained within this handle scope. + ALWAYS_INLINE uint32_t NumberOfReferences() const; + + ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const; + + template <typename Visitor> + ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); + + // Link to previous BaseHandleScope or null. + BaseHandleScope* GetLink() const { + return link_; + } + + ALWAYS_INLINE VariableSizedHandleScope* AsVariableSized(); + ALWAYS_INLINE HandleScope* AsHandleScope(); + ALWAYS_INLINE const VariableSizedHandleScope* AsVariableSized() const; + ALWAYS_INLINE const HandleScope* AsHandleScope() const; + + protected: + BaseHandleScope(BaseHandleScope* link, uint32_t num_references) + : link_(link), + number_of_references_(num_references) {} + + // Variable sized constructor. + BaseHandleScope(BaseHandleScope* link) + : link_(link), + number_of_references_(kNumReferencesVariableSized) {} + + static constexpr int32_t kNumReferencesVariableSized = -1; + + // Link-list of handle scopes. The root is held by a Thread. + BaseHandleScope* const link_; + + // Number of handlerized references. -1 for variable sized handle scopes. + const int32_t number_of_references_; + + private: + DISALLOW_COPY_AND_ASSIGN(BaseHandleScope); +}; // HandleScopes are scoped objects containing a number of Handles. They are used to allocate // handles, for these handles (and the objects contained within them) to be visible/roots for the // GC. It is most common to stack allocate HandleScopes using StackHandleScope. -class PACKED(4) HandleScope { +class PACKED(4) HandleScope : public BaseHandleScope { public: ~HandleScope() {} - // Number of references contained within this handle scope. - uint32_t NumberOfReferences() const { - return number_of_references_; - } - // We have versions with and without explicit pointer size of the following. The first two are // used at runtime, so OFFSETOF_MEMBER computes the right offsets automatically. The last one // takes the pointer size explicitly so that at compile time we can cross-compile correctly. @@ -59,11 +102,6 @@ class PACKED(4) HandleScope { // Returns the size of a HandleScope containing num_references handles. static size_t SizeOf(PointerSize pointer_size, uint32_t num_references); - // Link to previous HandleScope or null. - HandleScope* GetLink() const { - return link_; - } - ALWAYS_INLINE mirror::Object* GetReference(size_t i) const REQUIRES_SHARED(Locks::mutator_lock_); @@ -93,11 +131,26 @@ class PACKED(4) HandleScope { } // Placement new creation. - static HandleScope* Create(void* storage, HandleScope* link, uint32_t num_references) + static HandleScope* Create(void* storage, BaseHandleScope* link, uint32_t num_references) WARN_UNUSED { return new (storage) HandleScope(link, num_references); } + // Number of references contained within this handle scope. + ALWAYS_INLINE uint32_t NumberOfReferences() const { + DCHECK_GE(number_of_references_, 0); + return static_cast<uint32_t>(number_of_references_); + } + + template <typename Visitor> + void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) { + for (size_t i = 0, count = NumberOfReferences(); i < count; ++i) { + // GetReference returns a pointer to the stack reference within the handle scope. If this + // needs to be updated, it will be done by the root visitor. + visitor.VisitRootIfNonNull(GetHandle(i).GetReference()); + } + } + protected: // Return backing storage used for references. ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const { @@ -105,20 +158,11 @@ class PACKED(4) HandleScope { return reinterpret_cast<StackReference<mirror::Object>*>(address); } - explicit HandleScope(size_t number_of_references) : - link_(nullptr), number_of_references_(number_of_references) { - } + explicit HandleScope(size_t number_of_references) : HandleScope(nullptr, number_of_references) {} // Semi-hidden constructor. Construction expected by generated code and StackHandleScope. - HandleScope(HandleScope* link, uint32_t num_references) : - link_(link), number_of_references_(num_references) { - } - - // Link-list of handle scopes. The root is held by a Thread. - HandleScope* const link_; - - // Number of handlerized references. - const uint32_t number_of_references_; + HandleScope(BaseHandleScope* link, uint32_t num_references) + : BaseHandleScope(link, num_references) {} // Storage for references. // StackReference<mirror::Object> references_[number_of_references_] @@ -165,14 +209,10 @@ class HandleWrapperObjPtr : public MutableHandle<T> { ObjPtr<T>* const obj_; }; - -// Scoped handle storage of a fixed size that is usually stack allocated. +// Fixed size handle scope that is not necessarily linked in the thread. template<size_t kNumReferences> -class PACKED(4) StackHandleScope FINAL : public HandleScope { +class PACKED(4) FixedSizeHandleScope : public HandleScope { public: - explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr); - ALWAYS_INLINE ~StackHandleScope(); - template<class T> ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_); @@ -191,11 +231,15 @@ class PACKED(4) StackHandleScope FINAL : public HandleScope { ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_); - Thread* Self() const { - return self_; + size_t RemainingSlots() const { + return kNumReferences - pos_; } private: + explicit ALWAYS_INLINE FixedSizeHandleScope(BaseHandleScope* link, + mirror::Object* fill_value = nullptr); + ALWAYS_INLINE ~FixedSizeHandleScope() {} + template<class T> ALWAYS_INLINE MutableHandle<T> GetHandle(size_t i) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_LT(i, kNumReferences); @@ -205,66 +249,65 @@ class PACKED(4) StackHandleScope FINAL : public HandleScope { // Reference storage needs to be first as expected by the HandleScope layout. StackReference<mirror::Object> storage_[kNumReferences]; - // The thread that the stack handle scope is a linked list upon. The stack handle scope will - // push and pop itself from this thread. - Thread* const self_; - // Position new handles will be created. - size_t pos_; + size_t pos_ = 0; template<size_t kNumRefs> friend class StackHandleScope; + friend class VariableSizedHandleScope; }; -// Utility class to manage a collection (stack) of StackHandleScope. All the managed -// scope handle have the same fixed sized. -// Calls to NewHandle will create a new handle inside the top StackHandleScope. -// When the handle scope becomes full a new one is created and push on top of the -// previous. -// -// NB: -// - it is not safe to use the *same* StackHandleScopeCollection intermix with -// other StackHandleScopes. -// - this is a an easy way around implementing a full ZoneHandleScope to manage an -// arbitrary number of handles. -class StackHandleScopeCollection { +// Scoped handle storage of a fixed size that is stack allocated. +template<size_t kNumReferences> +class PACKED(4) StackHandleScope FINAL : public FixedSizeHandleScope<kNumReferences> { public: - explicit StackHandleScopeCollection(Thread* const self) : - self_(self), - current_scope_num_refs_(0) { - } + explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr); + ALWAYS_INLINE ~StackHandleScope(); - ~StackHandleScopeCollection() { - while (!scopes_.empty()) { - delete scopes_.top(); - scopes_.pop(); - } + Thread* Self() const { + return self_; } + private: + // The thread that the stack handle scope is a linked list upon. The stack handle scope will + // push and pop itself from this thread. + Thread* const self_; +}; + +// Utility class to manage a variable sized handle scope by having a list of fixed size handle +// scopes. +// Calls to NewHandle will create a new handle inside the current FixedSizeHandleScope. +// When the current handle scope becomes full a new one is created and put at the front of the +// list. +class VariableSizedHandleScope : public BaseHandleScope { + public: + explicit VariableSizedHandleScope(Thread* const self); + ~VariableSizedHandleScope(); + template<class T> - MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) { - if (scopes_.empty() || current_scope_num_refs_ >= kNumReferencesPerScope) { - StackHandleScope<kNumReferencesPerScope>* scope = - new StackHandleScope<kNumReferencesPerScope>(self_); - scopes_.push(scope); - current_scope_num_refs_ = 0; - } - current_scope_num_refs_++; - return scopes_.top()->NewHandle(object); - } + MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_); template<class MirrorType, bool kPoison> MutableHandle<MirrorType> NewHandle(ObjPtr<MirrorType, kPoison> ptr) REQUIRES_SHARED(Locks::mutator_lock_); + // Number of references contained within this handle scope. + ALWAYS_INLINE uint32_t NumberOfReferences() const; + + ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const; + + template <typename Visitor> + void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); + private: static constexpr size_t kNumReferencesPerScope = 4; Thread* const self_; - std::stack<StackHandleScope<kNumReferencesPerScope>*> scopes_; - size_t current_scope_num_refs_; + // Linked list of fixed size handle scopes. + using LocalScopeType = FixedSizeHandleScope<kNumReferencesPerScope>; + LocalScopeType* current_scope_; - DISALLOW_COPY_AND_ASSIGN(StackHandleScopeCollection); + DISALLOW_COPY_AND_ASSIGN(VariableSizedHandleScope); }; } // namespace art diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc index c269a37f8d..92063c4ba8 100644 --- a/runtime/handle_scope_test.cc +++ b/runtime/handle_scope_test.cc @@ -15,6 +15,7 @@ */ #include "base/enums.h" +#include "common_runtime_test.h" #include "gtest/gtest.h" #include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" @@ -22,51 +23,85 @@ namespace art { -// Handle scope with a fixed size which is allocated on the stack. -template<size_t kNumReferences> -class NoThreadStackHandleScope : public HandleScope { - public: - explicit NoThreadStackHandleScope(HandleScope* link) : HandleScope(link, kNumReferences) { - } - ~NoThreadStackHandleScope() { - } - - private: - // references_storage_ needs to be first so that it matches the address of references_ - StackReference<mirror::Object> references_storage_[kNumReferences]; -}; +class HandleScopeTest : public CommonRuntimeTest {}; // Test the offsets computed for members of HandleScope. Because of cross-compiling // it is impossible the use OFFSETOF_MEMBER, so we do some reasonable computations ourselves. This // test checks whether we do the right thing. -TEST(HandleScopeTest, Offsets) NO_THREAD_SAFETY_ANALYSIS { +TEST_F(HandleScopeTest, Offsets) { + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); // As the members of HandleScope are private, we cannot use OFFSETOF_MEMBER // here. So do the inverse: set some data, and access it through pointers created from the offsets. - NoThreadStackHandleScope<0x9ABC> test_table(reinterpret_cast<HandleScope*>(0x5678)); - test_table.SetReference(0, reinterpret_cast<mirror::Object*>(0x1234)); + StackHandleScope<0x1> hs0(soa.Self()); + static const size_t kNumReferences = 0x9ABC; + StackHandleScope<kNumReferences> test_table(soa.Self()); + ObjPtr<mirror::Class> c = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + test_table.SetReference(0, c.Ptr()); uint8_t* table_base_ptr = reinterpret_cast<uint8_t*>(&test_table); { - uintptr_t* link_ptr = reinterpret_cast<uintptr_t*>(table_base_ptr + + BaseHandleScope** link_ptr = reinterpret_cast<BaseHandleScope**>(table_base_ptr + HandleScope::LinkOffset(kRuntimePointerSize)); - EXPECT_EQ(*link_ptr, static_cast<size_t>(0x5678)); + EXPECT_EQ(*link_ptr, &hs0); } { uint32_t* num_ptr = reinterpret_cast<uint32_t*>(table_base_ptr + HandleScope::NumberOfReferencesOffset(kRuntimePointerSize)); - EXPECT_EQ(*num_ptr, static_cast<size_t>(0x9ABC)); + EXPECT_EQ(*num_ptr, static_cast<size_t>(kNumReferences)); } { - // Assume sizeof(StackReference<mirror::Object>) == sizeof(uint32_t) - // TODO: How can we make this assumption-less but still access directly and fully? - EXPECT_EQ(sizeof(StackReference<mirror::Object>), sizeof(uint32_t)); - - uint32_t* ref_ptr = reinterpret_cast<uint32_t*>(table_base_ptr + + auto* ref_ptr = reinterpret_cast<StackReference<mirror::Object>*>(table_base_ptr + HandleScope::ReferencesOffset(kRuntimePointerSize)); - EXPECT_EQ(*ref_ptr, static_cast<uint32_t>(0x1234)); + EXPECT_OBJ_PTR_EQ(ref_ptr->AsMirrorPtr(), c); + } +} + +class CollectVisitor { + public: + void VisitRootIfNonNull(StackReference<mirror::Object>* ref) { + if (!ref->IsNull()) { + visited.insert(ref); + } + ++total_visited; + } + + std::set<StackReference<mirror::Object>*> visited; + size_t total_visited = 0; // including null. +}; + +// Test functionality of variable sized handle scopes. +TEST_F(HandleScopeTest, VariableSized) { + ScopedObjectAccess soa(Thread::Current()); + VariableSizedHandleScope hs(soa.Self()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::Class> c = + hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;")); + // Test nested scopes. + StackHandleScope<1> inner(soa.Self()); + inner.NewHandle(c->AllocObject(soa.Self())); + // Add a bunch of handles and make sure callbacks work. + static const size_t kNumHandles = 100; + std::vector<Handle<mirror::Object>> handles; + for (size_t i = 0; i < kNumHandles; ++i) { + BaseHandleScope* base = &hs; + ObjPtr<mirror::Object> o = c->AllocObject(soa.Self()); + handles.push_back(hs.NewHandle(o)); + EXPECT_OBJ_PTR_EQ(o, handles.back().Get()); + EXPECT_TRUE(hs.Contains(handles.back().GetReference())); + EXPECT_TRUE(base->Contains(handles.back().GetReference())); + EXPECT_EQ(hs.NumberOfReferences(), base->NumberOfReferences()); + } + CollectVisitor visitor; + BaseHandleScope* base = &hs; + base->VisitRoots(visitor); + EXPECT_LE(visitor.visited.size(), base->NumberOfReferences()); + EXPECT_EQ(visitor.total_visited, base->NumberOfReferences()); + for (StackReference<mirror::Object>* ref : visitor.visited) { + EXPECT_TRUE(base->Contains(ref)); } } diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h index e05f8f307c..e357fa66a4 100644 --- a/runtime/indirect_reference_table-inl.h +++ b/runtime/indirect_reference_table-inl.h @@ -90,7 +90,7 @@ inline ObjPtr<mirror::Object> IndirectReferenceTable::Get(IndirectRef iref) cons } uint32_t idx = ExtractIndex(iref); ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>(); - VerifyObject(obj.Ptr()); + VerifyObject(obj); return obj; } diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index d59bb39ccf..6109ec6758 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -99,7 +99,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, ObjPtr<mirror::Object> size_t topIndex = segment_state_.parts.topIndex; CHECK(obj != nullptr); - VerifyObject(obj.Ptr()); + VerifyObject(obj); DCHECK(table_ != nullptr); DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f0a7c16146..273c67d167 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2481,7 +2481,7 @@ class JNI { // Sanity check: If elements is not the same as the java array's data, it better not be a // heap address. TODO: This might be slow to check, may be worth keeping track of which // copies we make? - if (heap->IsNonDiscontinuousSpaceHeapAddress(reinterpret_cast<mirror::Object*>(elements))) { + if (heap->IsNonDiscontinuousSpaceHeapAddress(elements)) { soa.Vm()->JniAbortF("ReleaseArrayElements", "invalid element pointer %p, array elements are %p", reinterpret_cast<void*>(elements), array_data); diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 9d7f98fe62..7cbcac8030 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -100,10 +100,10 @@ class SetLengthVisitor { explicit SetLengthVisitor(int32_t length) : length_(length) { } - void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid AsArray as object is not yet in live bitmap or allocation stack. - Array* array = down_cast<Array*>(obj); + ObjPtr<Array> array = ObjPtr<Array>::DownCast(obj); // DCHECK(array->IsArrayInstance()); array->SetLength(length_); } @@ -124,10 +124,10 @@ class SetLengthToUsableSizeVisitor { component_size_shift_(component_size_shift) { } - void operator()(Object* obj, size_t usable_size) const + void operator()(ObjPtr<Object> obj, size_t usable_size) const REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid AsArray as object is not yet in live bitmap or allocation stack. - Array* array = down_cast<Array*>(obj); + ObjPtr<Array> array = ObjPtr<Array>::DownCast(obj); // DCHECK(array->IsArrayInstance()); int32_t length = (usable_size - header_size_) >> component_size_shift_; DCHECK_GE(length, minimum_length_); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index cc088b8aa8..98d383dac9 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -707,9 +707,13 @@ inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) { if (!kCheckAddFinalizer) { DCHECK(!IsFinalizable()); } - mirror::Object* obj = - heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, this, this->object_size_, - allocator_type, VoidFunctor()); + // Note that the this pointer may be invalidated after the allocation. + ObjPtr<mirror::Object> obj = + heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, + this, + this->object_size_, + allocator_type, + VoidFunctor()); if (add_finalizer && LIKELY(obj != nullptr)) { heap->AddFinalizerReference(self, &obj); if (UNLIKELY(self->IsExceptionPending())) { @@ -717,7 +721,7 @@ inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) { obj = nullptr; } } - return obj; + return obj.Ptr(); } inline Object* Class::AllocObject(Thread* self) { @@ -879,11 +883,11 @@ inline void Class::SetSlowPath(bool enabled) { SetFieldBoolean<false, false>(GetSlowPathFlagOffset(), enabled); } -inline void Class::InitializeClassVisitor::operator()( - mirror::Object* obj, size_t usable_size) const { +inline void Class::InitializeClassVisitor::operator()(ObjPtr<mirror::Object> obj, + size_t usable_size) const { DCHECK_LE(class_size_, usable_size); // Avoid AsClass as object is not yet in live bitmap or allocation stack. - mirror::Class* klass = down_cast<mirror::Class*>(obj); + ObjPtr<mirror::Class> klass = ObjPtr<mirror::Class>::DownCast(obj); // DCHECK(klass->IsClass()); klass->SetClassSize(class_size_); klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive. diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 40742d2731..689dd227c6 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -998,11 +998,11 @@ class CopyClassVisitor { copy_bytes_(copy_bytes), imt_(imt), pointer_size_(pointer_size) { } - void operator()(mirror::Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<mirror::Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self_); Handle<mirror::Class> h_new_class_obj(hs.NewHandle(obj->AsClass())); - mirror::Object::CopyObject(self_, h_new_class_obj.Get(), orig_->Get(), copy_bytes_); + mirror::Object::CopyObject(h_new_class_obj.Get(), orig_->Get(), copy_bytes_); mirror::Class::SetStatus(h_new_class_obj, Class::kStatusResolving, self_); h_new_class_obj->PopulateEmbeddedVTable(pointer_size_); h_new_class_obj->SetImt(imt_, pointer_size_); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index a0d6f37672..725939a7c4 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1232,7 +1232,7 @@ class MANAGED Class FINAL : public Object { explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) { } - void operator()(mirror::Object* obj, size_t usable_size) const + void operator()(ObjPtr<mirror::Object> obj, size_t usable_size) const REQUIRES_SHARED(Locks::mutator_lock_); private: diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 9d3c26ec3c..daee727769 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -43,10 +43,10 @@ Atomic<uint32_t> Object::hash_code_seed(987654321U + std::time(nullptr)); class CopyReferenceFieldsWithReadBarrierVisitor { public: - explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj) + explicit CopyReferenceFieldsWithReadBarrierVisitor(ObjPtr<Object> dest_obj) : dest_obj_(dest_obj) {} - void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const + void operator()(ObjPtr<Object> obj, MemberOffset offset, bool /* is_static */) const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { // GetFieldObject() contains a RB. Object* ref = obj->GetFieldObject<Object>(offset); @@ -55,7 +55,7 @@ class CopyReferenceFieldsWithReadBarrierVisitor { dest_obj_->SetFieldObjectWithoutWriteBarrier<false, false>(offset, ref); } - void operator()(mirror::Class* klass, mirror::Reference* ref) const + void operator()(ObjPtr<mirror::Class> klass, mirror::Reference* ref) const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { // Copy java.lang.ref.Reference.referent which isn't visited in // Object::VisitReferences(). @@ -69,18 +69,17 @@ class CopyReferenceFieldsWithReadBarrierVisitor { void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} private: - Object* const dest_obj_; + ObjPtr<Object> const dest_obj_; }; -Object* Object::CopyObject(Thread* self, - mirror::Object* dest, - mirror::Object* src, +Object* Object::CopyObject(ObjPtr<mirror::Object> dest, + ObjPtr<mirror::Object> src, size_t num_bytes) { // Copy instance data. Don't assume memcpy copies by words (b/32012820). { const size_t offset = sizeof(Object); - uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src) + offset; - uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest) + offset; + uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src.Ptr()) + offset; + uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest.Ptr()) + offset; num_bytes -= offset; DCHECK_ALIGNED(src_bytes, sizeof(uintptr_t)); DCHECK_ALIGNED(dst_bytes, sizeof(uintptr_t)); @@ -128,26 +127,21 @@ Object* Object::CopyObject(Thread* self, } else { heap->WriteBarrierEveryFieldOf(dest); } - if (c->IsFinalizable()) { - heap->AddFinalizerReference(self, &dest); - } - return dest; + return dest.Ptr(); } // An allocation pre-fence visitor that copies the object. class CopyObjectVisitor { public: - CopyObjectVisitor(Thread* self, Handle<Object>* orig, size_t num_bytes) - : self_(self), orig_(orig), num_bytes_(num_bytes) { - } + CopyObjectVisitor(Handle<Object>* orig, size_t num_bytes) + : orig_(orig), num_bytes_(num_bytes) {} - void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { - Object::CopyObject(self_, obj, orig_->Get(), num_bytes_); + Object::CopyObject(obj, orig_->Get(), num_bytes_); } private: - Thread* const self_; Handle<Object>* const orig_; const size_t num_bytes_; DISALLOW_COPY_AND_ASSIGN(CopyObjectVisitor); @@ -161,14 +155,17 @@ Object* Object::Clone(Thread* self) { size_t num_bytes = SizeOf(); StackHandleScope<1> hs(self); Handle<Object> this_object(hs.NewHandle(this)); - Object* copy; - CopyObjectVisitor visitor(self, &this_object, num_bytes); + ObjPtr<Object> copy; + CopyObjectVisitor visitor(&this_object, num_bytes); if (heap->IsMovableObject(this)) { copy = heap->AllocObject<true>(self, GetClass(), num_bytes, visitor); } else { copy = heap->AllocNonMovableObject<true>(self, GetClass(), num_bytes, visitor); } - return copy; + if (this_object->GetClass()->IsFinalizable()) { + heap->AddFinalizerReference(self, ©); + } + return copy.Ptr(); } uint32_t Object::GenerateIdentityHashCode() { diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 9ddf99500e..84aa96cbd7 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -609,10 +609,11 @@ class MANAGED LOCKABLE Object { } } - // A utility function that copies an object in a read barrier and - // write barrier-aware way. This is internally used by Clone() and - // Class::CopyOf(). - static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src, + // A utility function that copies an object in a read barrier and write barrier-aware way. + // This is internally used by Clone() and Class::CopyOf(). If the object is finalizable, + // it is the callers job to call Heap::AddFinalizerReference. + static Object* CopyObject(ObjPtr<mirror::Object> dest, + ObjPtr<mirror::Object> src, size_t num_bytes) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index aea6ff1579..cf902af0c0 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -43,10 +43,10 @@ class SetStringCountVisitor { explicit SetStringCountVisitor(int32_t count) : count_(count) { } - void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid AsString as object is not yet in live bitmap or allocation stack. - String* string = down_cast<String*>(obj); + ObjPtr<String> string = ObjPtr<String>::DownCast(obj); string->SetCount(count_); DCHECK(!string->IsCompressed() || kUseStringCompression); } @@ -63,10 +63,10 @@ class SetStringCountAndBytesVisitor { : count_(count), src_array_(src_array), offset_(offset), high_byte_(high_byte) { } - void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid AsString as object is not yet in live bitmap or allocation stack. - String* string = down_cast<String*>(obj); + ObjPtr<String> string = ObjPtr<String>::DownCast(obj); string->SetCount(count_); DCHECK(!string->IsCompressed() || kUseStringCompression); int32_t length = String::GetLengthFromCount(count_); @@ -99,10 +99,10 @@ class SetStringCountAndValueVisitorFromCharArray { count_(count), src_array_(src_array), offset_(offset) { } - void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid AsString as object is not yet in live bitmap or allocation stack. - String* string = down_cast<String*>(obj); + ObjPtr<String> string = ObjPtr<String>::DownCast(obj); string->SetCount(count_); const uint16_t* const src = src_array_->GetData() + offset_; const int32_t length = String::GetLengthFromCount(count_); @@ -131,10 +131,10 @@ class SetStringCountAndValueVisitorFromString { count_(count), src_string_(src_string), offset_(offset) { } - void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const + void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid AsString as object is not yet in live bitmap or allocation stack. - String* string = down_cast<String*>(obj); + ObjPtr<String> string = ObjPtr<String>::DownCast(obj); string->SetCount(count_); const int32_t length = String::GetLengthFromCount(count_); bool compressible = kUseStringCompression && String::GetCompressionFlagFromCount(count_); diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 8e81bc98b6..18529561cf 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -254,7 +254,9 @@ static void VMDebug_infopoint(JNIEnv*, jclass, jint id) { LOG(INFO) << "VMDebug infopoint " << id << " hit"; } -static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, +static jlong VMDebug_countInstancesOfClass(JNIEnv* env, + jclass, + jclass javaClass, jboolean countAssignable) { ScopedObjectAccess soa(env); gc::Heap* const heap = Runtime::Current()->GetHeap(); @@ -263,13 +265,16 @@ static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass if (c == nullptr) { return 0; } - std::vector<mirror::Class*> classes {c.Ptr()}; + VariableSizedHandleScope hs(soa.Self()); + std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)}; uint64_t count = 0; heap->CountInstances(classes, countAssignable, &count); return count; } -static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses, +static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, + jclass, + jobjectArray javaClasses, jboolean countAssignable) { ScopedObjectAccess soa(env); gc::Heap* const heap = Runtime::Current()->GetHeap(); @@ -279,14 +284,15 @@ static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectAr if (decoded_classes == nullptr) { return nullptr; } - std::vector<mirror::Class*> classes; + VariableSizedHandleScope hs(soa.Self()); + std::vector<Handle<mirror::Class>> classes; for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) { - classes.push_back(decoded_classes->Get(i)); + classes.push_back(hs.NewHandle(decoded_classes->Get(i))); } std::vector<uint64_t> counts(classes.size(), 0u); // Heap::CountInstances can handle null and will put 0 for these classes. heap->CountInstances(classes, countAssignable, &counts[0]); - auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); + ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); if (long_counts == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index c20c8b88a4..6b9468d3e1 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -296,10 +296,10 @@ void DumpNativeStack(std::ostream& os, std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map)); if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) { os << prefix << "(backtrace::Unwind failed for thread " << tid - << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")\n"; + << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl; return; } else if (backtrace->NumFrames() == 0) { - os << prefix << "(no native stack frames for thread " << tid << ")\n"; + os << prefix << "(no native stack frames for thread " << tid << ")" << std::endl; return; } @@ -354,7 +354,7 @@ void DumpNativeStack(std::ostream& os, } os << ")"; } - os << "\n"; + os << std::endl; if (try_addr2line && use_addr2line) { Addr2line(it->map.name, it->pc - it->map.start, os, prefix, &addr2line_state); } @@ -395,7 +395,7 @@ void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix, bool inclu if (include_count) { os << StringPrintf("#%02zd ", i); } - os << text << "\n"; + os << text << std::endl; } } diff --git a/runtime/obj_ptr-inl.h b/runtime/obj_ptr-inl.h index f0a5f6f2e2..d0be6dc981 100644 --- a/runtime/obj_ptr-inl.h +++ b/runtime/obj_ptr-inl.h @@ -33,7 +33,7 @@ inline bool ObjPtr<MirrorType, kPoison>::IsValid() const { template<class MirrorType, bool kPoison> inline void ObjPtr<MirrorType, kPoison>::AssertValid() const { if (kPoison) { - CHECK(IsValid()) << "Stale object pointer " << Ptr() << " , expected cookie " + CHECK(IsValid()) << "Stale object pointer " << PtrUnchecked() << " , expected cookie " << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie(); } } diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 2d9149a1c5..de6683cc83 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -18,6 +18,7 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: ["events.cc", + "heap.cc", "object_tagging.cc", "OpenjdkJvmTi.cc", "transform.cc"], diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 36be2a0127..05da585b3a 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -39,6 +39,7 @@ #include "art_jvmti.h" #include "base/mutex.h" #include "events-inl.h" +#include "heap.h" #include "jni_env_ext-inl.h" #include "object_tagging.h" #include "obj_ptr-inl.h" @@ -276,7 +277,8 @@ class JvmtiFunctions { jclass klass, const jvmtiHeapCallbacks* callbacks, const void* user_data) { - return ERR(NOT_IMPLEMENTED); + HeapUtil heap_util(&gObjectTagTable); + return heap_util.IterateThroughHeap(env, heap_filter, klass, callbacks, user_data); } static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) { diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index 4d5b7e0c7e..450f85e51a 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -128,11 +128,13 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { public: explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {} - void ObjectAllocated(art::Thread* self, art::mirror::Object** obj, size_t byte_count) + void ObjectAllocated(art::Thread* self, art::ObjPtr<art::mirror::Object>* obj, size_t byte_count) REQUIRES_SHARED(art::Locks::mutator_lock_) { DCHECK_EQ(self, art::Thread::Current()); if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) { + art::StackHandleScope<1> hs(self); + auto h = hs.NewHandleWrapper(obj); // jvmtiEventVMObjectAlloc parameters: // jvmtiEnv *jvmti_env, // JNIEnv* jni_env, @@ -153,7 +155,7 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { ScopedLocalRef<jobject> object( jni_env, jni_env->AddLocalReference<jobject>(*obj)); ScopedLocalRef<jclass> klass( - jni_env, jni_env->AddLocalReference<jclass>((*obj)->GetClass())); + jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass())); handler_->DispatchEvent(self, JVMTI_EVENT_VM_OBJECT_ALLOC, diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/heap.cc new file mode 100644 index 0000000000..95d9a1d315 --- /dev/null +++ b/runtime/openjdkjvmti/heap.cc @@ -0,0 +1,166 @@ +/* + * 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. + */ + +#include "heap.h" + +#include "art_jvmti.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "gc/heap.h" +#include "mirror/class.h" +#include "object_callbacks.h" +#include "object_tagging.h" +#include "obj_ptr-inl.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +struct IterateThroughHeapData { + IterateThroughHeapData(HeapUtil* _heap_util, + jint heap_filter, + art::ObjPtr<art::mirror::Class> klass, + const jvmtiHeapCallbacks* _callbacks, + const void* _user_data) + : heap_util(_heap_util), + filter_klass(klass), + callbacks(_callbacks), + user_data(_user_data), + filter_out_tagged((heap_filter & JVMTI_HEAP_FILTER_TAGGED) != 0), + filter_out_untagged((heap_filter & JVMTI_HEAP_FILTER_UNTAGGED) != 0), + filter_out_class_tagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_TAGGED) != 0), + filter_out_class_untagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) != 0), + any_filter(filter_out_tagged || + filter_out_untagged || + filter_out_class_tagged || + filter_out_class_untagged), + stop_reports(false) { + } + + bool ShouldReportByHeapFilter(jlong tag, jlong class_tag) { + if (!any_filter) { + return true; + } + + if ((tag == 0 && filter_out_untagged) || (tag != 0 && filter_out_tagged)) { + return false; + } + + if ((class_tag == 0 && filter_out_class_untagged) || + (class_tag != 0 && filter_out_class_tagged)) { + return false; + } + + return true; + } + + HeapUtil* heap_util; + art::ObjPtr<art::mirror::Class> filter_klass; + const jvmtiHeapCallbacks* callbacks; + const void* user_data; + const bool filter_out_tagged; + const bool filter_out_untagged; + const bool filter_out_class_tagged; + const bool filter_out_class_untagged; + const bool any_filter; + + bool stop_reports; +}; + +static void IterateThroughHeapObjectCallback(art::mirror::Object* obj, void* arg) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + IterateThroughHeapData* ithd = reinterpret_cast<IterateThroughHeapData*>(arg); + // Early return, as we can't really stop visiting. + if (ithd->stop_reports) { + return; + } + + art::ScopedAssertNoThreadSuspension no_suspension("IterateThroughHeapCallback"); + + jlong tag = 0; + ithd->heap_util->GetTags()->GetTag(obj, &tag); + + jlong class_tag = 0; + art::ObjPtr<art::mirror::Class> klass = obj->GetClass(); + ithd->heap_util->GetTags()->GetTag(klass.Ptr(), &class_tag); + // For simplicity, even if we find a tag = 0, assume 0 = not tagged. + + if (!ithd->ShouldReportByHeapFilter(tag, class_tag)) { + return; + } + + // TODO: Handle array_primitive_value_callback. + + if (ithd->filter_klass != nullptr) { + if (ithd->filter_klass != klass) { + return; + } + } + + jlong size = obj->SizeOf(); + + jint length = -1; + if (obj->IsArrayInstance()) { + length = obj->AsArray()->GetLength(); + } + + jlong saved_tag = tag; + jint ret = ithd->callbacks->heap_iteration_callback(class_tag, + size, + &tag, + length, + const_cast<void*>(ithd->user_data)); + + if (tag != saved_tag) { + ithd->heap_util->GetTags()->Set(obj, tag); + } + + ithd->stop_reports = (ret & JVMTI_VISIT_ABORT) != 0; + + // TODO Implement array primitive and string primitive callback. + // TODO Implement primitive field callback. +} + +jvmtiError HeapUtil::IterateThroughHeap(jvmtiEnv* env ATTRIBUTE_UNUSED, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + if (callbacks == nullptr) { + return ERR(NULL_POINTER); + } + + if (callbacks->array_primitive_value_callback != nullptr) { + // TODO: Implement. + return ERR(NOT_IMPLEMENTED); + } + + art::Thread* self = art::Thread::Current(); + art::ScopedObjectAccess soa(self); // Now we know we have the shared lock. + + IterateThroughHeapData ithd(this, + heap_filter, + soa.Decode<art::mirror::Class>(klass), + callbacks, + user_data); + + art::Runtime::Current()->GetHeap()->VisitObjects(IterateThroughHeapObjectCallback, &ithd); + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/heap.h b/runtime/openjdkjvmti/heap.h new file mode 100644 index 0000000000..fb9a2164ae --- /dev/null +++ b/runtime/openjdkjvmti/heap.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_HEAP_H_ +#define ART_RUNTIME_OPENJDKJVMTI_HEAP_H_ + +#include "jvmti.h" + +namespace openjdkjvmti { + +class ObjectTagTable; + +class HeapUtil { + public: + explicit HeapUtil(ObjectTagTable* tags) : tags_(tags) { + } + + jvmtiError IterateThroughHeap(jvmtiEnv* env, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + ObjectTagTable* GetTags() { + return tags_; + } + + private: + ObjectTagTable* tags_; +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_HEAP_H_ diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc index bb17cac476..29d483094b 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -87,6 +87,33 @@ bool ObjectTagTable::Remove(art::mirror::Object* obj, jlong* tag) { return false; } +bool ObjectTagTable::Set(art::mirror::Object* obj, jlong new_tag) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + for (auto& pair : tagged_objects_) { + if (pair.first.Read(nullptr) == obj) { + pair.second = new_tag; + return true; + } + } + + // TODO refactor with Add. + if (first_free_ == tagged_objects_.size()) { + tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), new_tag)); + first_free_++; + } else { + DCHECK_LT(first_free_, tagged_objects_.size()); + DCHECK(tagged_objects_[first_free_].first.IsNull()); + tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), new_tag); + // TODO: scan for free elements. + first_free_ = tagged_objects_.size(); + } + + return false; +} + void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) { if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) { SweepImpl<true>(visitor); diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h index 45f3e4f42d..b399e653ee 100644 --- a/runtime/openjdkjvmti/object_tagging.h +++ b/runtime/openjdkjvmti/object_tagging.h @@ -42,6 +42,10 @@ class ObjectTagTable : public art::gc::SystemWeakHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_); + bool Set(art::mirror::Object* obj, jlong tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + bool GetTag(art::mirror::Object* obj, jlong* result) REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!allow_disallow_lock_) { diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index e0ba8e7489..a9f39d09b4 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -41,7 +41,7 @@ ReferenceTable::~ReferenceTable() { void ReferenceTable::Add(ObjPtr<mirror::Object> obj) { DCHECK(obj != nullptr); - VerifyObject(obj.Ptr()); + VerifyObject(obj); if (entries_.size() >= max_size_) { LOG(FATAL) << "ReferenceTable '" << name_ << "' " << "overflowed (" << max_size_ << " entries)"; diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc index aed6a2b1cf..be9786024a 100644 --- a/runtime/runtime_android.cc +++ b/runtime/runtime_android.cc @@ -27,7 +27,6 @@ namespace art { -static constexpr bool kDumpHeapObjectOnSigsevg = false; static constexpr bool kUseSignalHandler = false; struct sigaction old_action; @@ -48,11 +47,6 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex if (runtime != nullptr) { // Print this out first in case DumpObject faults. LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage(); - gc::Heap* heap = runtime->GetHeap(); - if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) { - LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: "; - heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr)); - } } // Run the old signal handler. old_action.sa_sigaction(signal_number, info, raw_context); diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index cee73e175a..93704a971c 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -21,6 +21,7 @@ #include <sys/utsname.h> #include <inttypes.h> +#include <iostream> #include <sstream> #include "base/dumpable.h" @@ -35,7 +36,6 @@ namespace art { -static constexpr bool kDumpHeapObjectOnSigsevg = false; static constexpr bool kUseSigRTTimeout = true; static constexpr bool kDumpNativeStackOnTimeout = true; @@ -337,17 +337,21 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex UContext thread_context(raw_context); Backtrace thread_backtrace(raw_context); - LOG(FATAL_WITHOUT_ABORT) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n" - << StringPrintf("Fatal signal %d (%s), code %d (%s)", - signal_number, GetSignalName(signal_number), - info->si_code, - GetSignalCodeName(signal_number, info->si_code)) - << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << "\n" - << "OS: " << Dumpable<OsInfo>(os_info) << "\n" - << "Cmdline: " << cmd_line << "\n" - << "Thread: " << tid << " \"" << thread_name << "\"\n" - << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n" - << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace); + // Note: We are using cerr directly instead of LOG macros to ensure even just partial output + // makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable + // considering this is an abort situation. + + std::cerr << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n" + << StringPrintf("Fatal signal %d (%s), code %d (%s)", + signal_number, GetSignalName(signal_number), + info->si_code, + GetSignalCodeName(signal_number, info->si_code)) + << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << std::endl + << "OS: " << Dumpable<OsInfo>(os_info) << std::endl + << "Cmdline: " << cmd_line << std::endl + << "Thread: " << tid << " \"" << thread_name << "\"" << std::endl + << "Registers:\n" << Dumpable<UContext>(thread_context) << std::endl + << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << std::endl; if (kIsDebugBuild && signal_number == SIGSEGV) { PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); } @@ -357,23 +361,20 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex // Special timeout signal. Try to dump all threads. // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts // are of value here. - runtime->GetThreadList()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), kDumpNativeStackOnTimeout); - } - gc::Heap* heap = runtime->GetHeap(); - LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage(); - if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) { - LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: "; - heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr)); + runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); + std::cerr << std::endl; } + std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl; } if (getenv("debug_db_uid") != nullptr || getenv("art_wait_for_gdb_on_crash") != nullptr) { - LOG(FATAL_WITHOUT_ABORT) << "********************************************************\n" - << "* Process " << getpid() << " thread " << tid << " \"" << thread_name - << "\"" - << " has been suspended while crashing.\n" - << "* Attach gdb:\n" - << "* gdb -p " << tid << "\n" - << "********************************************************\n"; + std::cerr << "********************************************************\n" + << "* Process " << getpid() << " thread " << tid << " \"" << thread_name + << "\"" + << " has been suspended while crashing.\n" + << "* Attach gdb:\n" + << "* gdb -p " << tid << "\n" + << "********************************************************" + << std::endl; // Wait for debugger to attach. while (true) { } diff --git a/runtime/thread.cc b/runtime/thread.cc index 3e2ecfe55e..45d3e348d6 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1826,7 +1826,7 @@ void Thread::RemoveFromThreadGroup(ScopedObjectAccess& soa) { size_t Thread::NumHandleReferences() { size_t count = 0; - for (HandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) { + for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) { count += cur->NumberOfReferences(); } return count; @@ -1835,7 +1835,7 @@ size_t Thread::NumHandleReferences() { bool Thread::HandleScopeContains(jobject obj) const { StackReference<mirror::Object>* hs_entry = reinterpret_cast<StackReference<mirror::Object>*>(obj); - for (HandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) { + for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) { if (cur->Contains(hs_entry)) { return true; } @@ -1847,12 +1847,8 @@ bool Thread::HandleScopeContains(jobject obj) const { void Thread::HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id) { BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor( visitor, RootInfo(kRootNativeStack, thread_id)); - for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) { - for (size_t j = 0, count = cur->NumberOfReferences(); j < count; ++j) { - // GetReference returns a pointer to the stack reference within the handle scope. If this - // needs to be updated, it will be done by the root visitor. - buffered_visitor.VisitRootIfNonNull(cur->GetHandle(j).GetReference()); - } + for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) { + cur->VisitRoots(buffered_visitor); } } @@ -1875,7 +1871,7 @@ ObjPtr<mirror::Object> Thread::DecodeJObject(jobject obj) const { if (LIKELY(HandleScopeContains(obj))) { // Read from handle scope. result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr(); - VerifyObject(result.Ptr()); + VerifyObject(result); } else { tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj); expect_null = true; diff --git a/runtime/thread.h b/runtime/thread.h index 20b4cc144b..376a69c6c0 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -799,17 +799,17 @@ class Thread { void HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id) REQUIRES_SHARED(Locks::mutator_lock_); - HandleScope* GetTopHandleScope() { + BaseHandleScope* GetTopHandleScope() { return tlsPtr_.top_handle_scope; } - void PushHandleScope(HandleScope* handle_scope) { + void PushHandleScope(BaseHandleScope* handle_scope) { DCHECK_EQ(handle_scope->GetLink(), tlsPtr_.top_handle_scope); tlsPtr_.top_handle_scope = handle_scope; } - HandleScope* PopHandleScope() { - HandleScope* handle_scope = tlsPtr_.top_handle_scope; + BaseHandleScope* PopHandleScope() { + BaseHandleScope* handle_scope = tlsPtr_.top_handle_scope; DCHECK(handle_scope != nullptr); tlsPtr_.top_handle_scope = tlsPtr_.top_handle_scope->GetLink(); return handle_scope; @@ -1446,7 +1446,7 @@ class Thread { mirror::Object* monitor_enter_object; // Top of linked list of handle scopes or null for none. - HandleScope* top_handle_scope; + BaseHandleScope* top_handle_scope; // Needed to get the right ClassLoader in JNI_OnLoad, but also // useful for testing. diff --git a/runtime/verify_object-inl.h b/runtime/verify_object-inl.h index 4892b49533..43151dd425 100644 --- a/runtime/verify_object-inl.h +++ b/runtime/verify_object-inl.h @@ -29,7 +29,7 @@ inline void VerifyObject(ObjPtr<mirror::Object> obj) { if (kVerifyObjectSupport > kVerifyObjectModeDisabled && obj != nullptr) { if (kVerifyObjectSupport > kVerifyObjectModeFast) { // Slow object verification, try the heap right away. - Runtime::Current()->GetHeap()->VerifyObjectBody(obj.Ptr()); + Runtime::Current()->GetHeap()->VerifyObjectBody(obj); } else { // Fast object verification, only call the heap if our quick sanity tests fail. The heap will // print the diagnostic message. @@ -40,7 +40,7 @@ inline void VerifyObject(ObjPtr<mirror::Object> obj) { failed = failed || !VerifyClassClass(c); } if (UNLIKELY(failed)) { - Runtime::Current()->GetHeap()->VerifyObjectBody(obj.Ptr()); + Runtime::Current()->GetHeap()->VerifyObjectBody(obj); } } } diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java index 7acf0080f8..dca00bd2ec 100644 --- a/test/530-checker-loops2/src/Main.java +++ b/test/530-checker-loops2/src/Main.java @@ -111,6 +111,24 @@ public class Main { return result; } + /// CHECK-START: int Main.periodicXorSequence(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicXorSequence(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicXorSequence(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + k ^= 1; + } + return result; + } + /// CHECK-START: int Main.justRightUp1() BCE (before) /// CHECK-DAG: BoundsCheck // @@ -895,8 +913,9 @@ public class Main { expectEquals(0, periodicIdiom(-1)); for (int tc = 0; tc < 32; tc++) { int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) + if ((tc & 1) != 0) { expected += 1; + } expectEquals(expected, periodicIdiom(tc)); } @@ -904,8 +923,9 @@ public class Main { expectEquals(0, periodicSequence2(-1)); for (int tc = 0; tc < 32; tc++) { int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) + if ((tc & 1) != 0) { expected += 1; + } expectEquals(expected, periodicSequence2(tc)); } @@ -915,6 +935,16 @@ public class Main { expectEquals(tc * 16, periodicSequence4(tc)); } + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicXorSequence(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) { + expected += 1; + } + expectEquals(expected, periodicXorSequence(tc)); + } + // Large bounds. expectEquals(55, justRightUp1()); expectEquals(55, justRightUp2()); diff --git a/test/619-checker-current-method/expected.txt b/test/619-checker-current-method/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/619-checker-current-method/expected.txt diff --git a/test/619-checker-current-method/info.txt b/test/619-checker-current-method/info.txt new file mode 100644 index 0000000000..75f5213975 --- /dev/null +++ b/test/619-checker-current-method/info.txt @@ -0,0 +1,2 @@ +Checks that we don't store the current method when the compiled +code does not need it. diff --git a/test/619-checker-current-method/src/Main.java b/test/619-checker-current-method/src/Main.java new file mode 100644 index 0000000000..d829370d74 --- /dev/null +++ b/test/619-checker-current-method/src/Main.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 Main { + + // Check that there is no instruction storing to stack. + /// CHECK-START-X86: int Main.foo(int, int, int, int, int, int) disassembly (after) + /// CHECK-NOT: mov [{{\w+}}], {{\w+}} + + // Use enough parameters to ensure we'll need a frame. + public static int foo(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + } + + public static void main(String[] args) { + if (foo(1, 2, 3, 4, 5, 6) != 21) { + throw new Error("Expected 21"); + } + } +} diff --git a/test/906-iterate-heap/build b/test/906-iterate-heap/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/906-iterate-heap/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/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt new file mode 100644 index 0000000000..72cd47dd62 --- /dev/null +++ b/test/906-iterate-heap/expected.txt @@ -0,0 +1,2 @@ +[{tag=1, class-tag=0, size=8, length=-1}, {tag=2, class-tag=100, size=8, length=-1}, {tag=3, class-tag=100, size=8, length=-1}, {tag=4, class-tag=0, size=32, length=5}, {tag=100, class-tag=0, size=<class>, length=-1}] +[{tag=11, class-tag=0, size=8, length=-1}, {tag=12, class-tag=110, size=8, length=-1}, {tag=13, class-tag=110, size=8, length=-1}, {tag=14, class-tag=0, size=32, length=5}, {tag=110, class-tag=0, size=<class>, length=-1}] diff --git a/test/906-iterate-heap/info.txt b/test/906-iterate-heap/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/906-iterate-heap/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc new file mode 100644 index 0000000000..ab1d8d8cb1 --- /dev/null +++ b/test/906-iterate-heap/iterate_heap.cc @@ -0,0 +1,187 @@ +/* + * 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 "iterate_heap.h" + +#include <iostream> +#include <pthread.h> +#include <stdio.h> +#include <vector> + +#include "base/logging.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedPrimitiveArray.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test906IterateHeap { + +class IterationConfig { + public: + IterationConfig() {} + virtual ~IterationConfig() {} + + virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0; +}; + +static jint JNICALL HeapIterationCallback(jlong class_tag, + jlong size, + jlong* tag_ptr, + jint length, + void* user_data) { + IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data); + return config->Handle(class_tag, size, tag_ptr, length); +} + +static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config) { + jvmtiHeapCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); + callbacks.heap_iteration_callback = HeapIterationCallback; + + jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter, + klass_filter, + &callbacks, + config); + if (ret != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(ret, &err); + printf("Failure running IterateThroughHeap: %s\n", err); + return false; + } + return true; +} + +extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env ATTRIBUTE_UNUSED, + jclass klass ATTRIBUTE_UNUSED, + jint heap_filter, + jclass klass_filter, + jint stop_after) { + class CountIterationConfig : public IterationConfig { + public: + CountIterationConfig(jint _counter, jint _stop_after) + : counter(_counter), + stop_after(_stop_after) { + } + + jint Handle(jlong class_tag ATTRIBUTE_UNUSED, + jlong size ATTRIBUTE_UNUSED, + jlong* tag_ptr ATTRIBUTE_UNUSED, + jint length ATTRIBUTE_UNUSED) OVERRIDE { + counter++; + if (counter == stop_after) { + return JVMTI_VISIT_ABORT; + } + return 0; + } + + jint counter; + const jint stop_after; + }; + + CountIterationConfig config(0, stop_after); + Run(heap_filter, klass_filter, &config); + + if (config.counter > config.stop_after) { + printf("Error: more objects visited than signaled."); + } + + return config.counter; +} + + +extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapData(JNIEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jint heap_filter, + jclass klass_filter, + jlongArray class_tags, + jlongArray sizes, + jlongArray tags, + jintArray lengths) { + class DataIterationConfig : public IterationConfig { + public: + jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) OVERRIDE { + class_tags_.push_back(class_tag); + sizes_.push_back(size); + tags_.push_back(*tag_ptr); + lengths_.push_back(length); + + return 0; // Continue. + } + + std::vector<jlong> class_tags_; + std::vector<jlong> sizes_; + std::vector<jlong> tags_; + std::vector<jint> lengths_; + }; + + DataIterationConfig config; + if (!Run(heap_filter, klass_filter, &config)) { + return -1; + } + + ScopedLongArrayRW s_class_tags(env, class_tags); + ScopedLongArrayRW s_sizes(env, sizes); + ScopedLongArrayRW s_tags(env, tags); + ScopedIntArrayRW s_lengths(env, lengths); + + for (size_t i = 0; i != config.class_tags_.size(); ++i) { + s_class_tags[i] = config.class_tags_[i]; + s_sizes[i] = config.sizes_[i]; + s_tags[i] = config.tags_[i]; + s_lengths[i] = config.lengths_[i]; + } + + return static_cast<jint>(config.class_tags_.size()); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env ATTRIBUTE_UNUSED, + jclass klass ATTRIBUTE_UNUSED, + jint heap_filter, + jclass klass_filter) { + class AddIterationConfig : public IterationConfig { + public: + AddIterationConfig() {} + + jint Handle(jlong class_tag ATTRIBUTE_UNUSED, + jlong size ATTRIBUTE_UNUSED, + jlong* tag_ptr, + jint length ATTRIBUTE_UNUSED) OVERRIDE { + jlong current_tag = *tag_ptr; + if (current_tag != 0) { + *tag_ptr = current_tag + 10; + } + return 0; + } + }; + + AddIterationConfig config; + Run(heap_filter, klass_filter, &config); +} + +// Don't do anything +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; + } + return 0; +} + +} // namespace Test906IterateHeap +} // namespace art diff --git a/test/906-iterate-heap/iterate_heap.h b/test/906-iterate-heap/iterate_heap.h new file mode 100644 index 0000000000..f25cdbaf49 --- /dev/null +++ b/test/906-iterate-heap/iterate_heap.h @@ -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. + */ + +#ifndef ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_ +#define ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_ + +#include <jni.h> + +namespace art { +namespace Test906IterateHeap { + +jint OnLoad(JavaVM* vm, char* options, void* reserved); + +} // namespace Test906IterateHeap +} // namespace art + +#endif // ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_ diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run new file mode 100755 index 0000000000..3e135a378d --- /dev/null +++ b/test/906-iterate-heap/run @@ -0,0 +1,43 @@ +#!/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. + +plugin=libopenjdkjvmtid.so +agent=libtiagentd.so +lib=tiagentd +if [[ "$@" == *"-O"* ]]; then + agent=libtiagent.so + plugin=libopenjdkjvmti.so + lib=tiagent +fi + +if [[ "$@" == *"--jvm"* ]]; then + arg="jvm" +else + arg="art" +fi + +if [[ "$@" != *"--debuggable"* ]]; then + other_args=" -Xcompiler-option --debuggable " +else + other_args="" +fi + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --runtime-option -agentpath:${agent}=906-iterate-heap,${arg} \ + --android-runtime-option -Xplugin:${plugin} \ + ${other_args} \ + --args ${lib} diff --git a/test/906-iterate-heap/src/Main.java b/test/906-iterate-heap/src/Main.java new file mode 100644 index 0000000000..544a3656b2 --- /dev/null +++ b/test/906-iterate-heap/src/Main.java @@ -0,0 +1,146 @@ +/* + * 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; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + public static void doTest() throws Exception { + A a = new A(); + B b = new B(); + B b2 = new B(); + C c = new C(); + A[] aArray = new A[5]; + + setTag(a, 1); + setTag(b, 2); + setTag(b2, 3); + setTag(aArray, 4); + setTag(B.class, 100); + + int all = iterateThroughHeapCount(0, null, Integer.MAX_VALUE); + int tagged = iterateThroughHeapCount(HEAP_FILTER_OUT_UNTAGGED, null, Integer.MAX_VALUE); + int untagged = iterateThroughHeapCount(HEAP_FILTER_OUT_TAGGED, null, Integer.MAX_VALUE); + int taggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_UNTAGGED, null, + Integer.MAX_VALUE); + int untaggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_TAGGED, null, + Integer.MAX_VALUE); + + if (all != tagged + untagged) { + throw new IllegalStateException("Instances: " + all + " != " + tagged + " + " + untagged); + } + if (all != taggedClass + untaggedClass) { + throw new IllegalStateException("By class: " + all + " != " + taggedClass + " + " + + untaggedClass); + } + if (tagged != 5) { + throw new IllegalStateException(tagged + " tagged objects"); + } + if (taggedClass != 2) { + throw new IllegalStateException(tagged + " objects with tagged class"); + } + if (all == tagged) { + throw new IllegalStateException("All objects tagged"); + } + if (all == taggedClass) { + throw new IllegalStateException("All objects have tagged class"); + } + + long classTags[] = new long[100]; + long sizes[] = new long[100]; + long tags[] = new long[100]; + int lengths[] = new int[100]; + + int n = iterateThroughHeapData(HEAP_FILTER_OUT_UNTAGGED, null, classTags, sizes, tags, lengths); + System.out.println(sort(n, classTags, sizes, tags, lengths)); + + iterateThroughHeapAdd(HEAP_FILTER_OUT_UNTAGGED, null); + n = iterateThroughHeapData(HEAP_FILTER_OUT_UNTAGGED, null, classTags, sizes, tags, lengths); + System.out.println(sort(n, classTags, sizes, tags, lengths)); + } + + static class A { + } + + static class B { + } + + static class C { + } + + static class HeapElem implements Comparable<HeapElem> { + long classTag; + long size; + long tag; + int length; + + public int compareTo(HeapElem other) { + if (tag != other.tag) { + return Long.compare(tag, other.tag); + } + if (classTag != other.classTag) { + return Long.compare(classTag, other.classTag); + } + if (size != other.size) { + return Long.compare(size, other.size); + } + return Integer.compare(length, other.length); + } + + public String toString() { + return "{tag=" + tag + ", class-tag=" + classTag + ", size=" + + (tag >= 100 ? "<class>" : size) // Class size is dependent on 32-bit vs 64-bit, + // so strip it. + + ", length=" + length + "}"; + } + } + + private static ArrayList<HeapElem> sort(int n, long classTags[], long sizes[], long tags[], + int lengths[]) { + ArrayList<HeapElem> ret = new ArrayList<HeapElem>(n); + for (int i = 0; i < n; i++) { + HeapElem elem = new HeapElem(); + elem.classTag = classTags[i]; + elem.size = sizes[i]; + elem.tag = tags[i]; + elem.length = lengths[i]; + ret.add(elem); + } + Collections.sort(ret); + return ret; + } + + private static native void setTag(Object o, long tag); + private static native long getTag(Object o); + + private final static int HEAP_FILTER_OUT_TAGGED = 0x4; + private final static int HEAP_FILTER_OUT_UNTAGGED = 0x8; + private final static int HEAP_FILTER_OUT_CLASS_TAGGED = 0x10; + private final static int HEAP_FILTER_OUT_CLASS_UNTAGGED = 0x20; + + private static native int iterateThroughHeapCount(int heapFilter, + Class<?> klassFilter, int stopAfter); + private static native int iterateThroughHeapData(int heapFilter, + Class<?> klassFilter, long classTags[], long sizes[], long tags[], int lengths[]); + private static native int iterateThroughHeapAdd(int heapFilter, + Class<?> klassFilter); +} diff --git a/test/Android.bp b/test/Android.bp index 4457e8a655..45673f55ff 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -238,8 +238,8 @@ art_cc_test_library { shared_libs: ["libartd"], } -art_cc_test_library { - name: "libtiagent", +art_cc_defaults { + name: "libtiagent-defaults", defaults: ["libartagent-defaults"], srcs: [ "ti-agent/common_load.cc", @@ -248,10 +248,18 @@ art_cc_test_library { "903-hello-tagging/tagging.cc", "904-object-allocation/tracking.cc", "905-object-free/tracking_free.cc", + "906-iterate-heap/iterate_heap.cc", ], shared_libs: [ - "libart", "libbase", + ], +} + +art_cc_test_library { + name: "libtiagent", + defaults: ["libtiagent-defaults"], + shared_libs: [ + "libart", "libopenjdkjvmti", ], } @@ -259,20 +267,11 @@ art_cc_test_library { art_cc_test_library { name: "libtiagentd", defaults: [ - "libartagent-defaults", + "libtiagent-defaults", "art_debug_defaults", ], - srcs: [ - "ti-agent/common_load.cc", - "901-hello-ti-agent/basics.cc", - "902-hello-transformation/transform.cc", - "903-hello-tagging/tagging.cc", - "904-object-allocation/tracking.cc", - "905-object-free/tracking_free.cc", - ], shared_libs: [ "libartd", - "libbase", "libopenjdkjvmtid", ], } diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 29000f053e..64ff5ba2f2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -263,7 +263,7 @@ endif # 147-stripped-dex-fallback isn't supported on device because --strip-dex # requires the zip command. # 569-checker-pattern-replacement tests behaviour present only on host. -# 90{2,3,4,5} are not supported in current form due to linker +# 90{2,3,4,5,6} are not supported in current form due to linker # restrictions. See b/31681198 TEST_ART_BROKEN_TARGET_TESTS := \ 147-stripped-dex-fallback \ @@ -272,6 +272,7 @@ TEST_ART_BROKEN_TARGET_TESTS := \ 903-hello-tagging \ 904-object-allocation \ 905-object-free \ + 906-iterate-heap \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -487,8 +488,11 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := # Known broken tests for the JIT. # CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT # also uses Generic JNI instead of the JNI compiler. +# Test 906 iterates the heap filtering with different options. No instances should be created +# between those runs to be able to have precise checks. TEST_ART_BROKEN_JIT_RUN_TESTS := \ - 137-cfi + 137-cfi \ + 906-iterate-heap \ ifneq (,$(filter jit,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 2b8947ec96..c4126365fc 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -29,6 +29,7 @@ #include "903-hello-tagging/tagging.h" #include "904-object-allocation/tracking.h" #include "905-object-free/tracking_free.h" +#include "906-iterate-heap/iterate_heap.h" namespace art { @@ -50,6 +51,7 @@ AgentLib agents[] = { { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr }, { "904-object-allocation", Test904ObjectAllocation::OnLoad, nullptr }, { "905-object-free", Test905ObjectFree::OnLoad, nullptr }, + { "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { |