diff options
30 files changed, 372 insertions, 260 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b29054dcd2..7fa272acee 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -69,7 +69,7 @@ using helpers::InputCPURegisterOrZeroRegAt; using helpers::InputFPRegisterAt; using helpers::InputOperandAt; using helpers::InputRegisterAt; -using helpers::Int64ConstantFrom; +using helpers::Int64FromLocation; using helpers::IsConstantZeroBitPattern; using helpers::LocationFrom; using helpers::OperandFromMemOperand; @@ -2704,7 +2704,7 @@ void LocationsBuilderARM64::VisitIntermediateAddressIndex(HIntermediateAddressIn void InstructionCodeGeneratorARM64::VisitIntermediateAddressIndex( HIntermediateAddressIndex* instruction) { Register index_reg = InputRegisterAt(instruction, 0); - uint32_t shift = Int64ConstantFrom(instruction->GetLocations()->InAt(2)); + uint32_t shift = Int64FromLocation(instruction->GetLocations()->InAt(2)); uint32_t offset = instruction->GetOffset()->AsIntConstant()->GetValue(); if (shift == 0) { @@ -2834,7 +2834,7 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); if (index.IsConstant()) { // Array load with a constant index can be treated as a field load. - offset += Int64ConstantFrom(index) << DataType::SizeShift(type); + offset += Int64FromLocation(index) << DataType::SizeShift(type); Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location::NoLocation(); codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, @@ -2879,14 +2879,14 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { "Expecting 0=compressed, 1=uncompressed"); __ Tbnz(length.W(), 0, &uncompressed_load); __ Ldrb(Register(OutputCPURegister(instruction)), - HeapOperand(obj, offset + Int64ConstantFrom(index))); + HeapOperand(obj, offset + Int64FromLocation(index))); __ B(&done); __ Bind(&uncompressed_load); __ Ldrh(Register(OutputCPURegister(instruction)), - HeapOperand(obj, offset + (Int64ConstantFrom(index) << 1))); + HeapOperand(obj, offset + (Int64FromLocation(index) << 1))); __ Bind(&done); } else { - offset += Int64ConstantFrom(index) << DataType::SizeShift(type); + offset += Int64FromLocation(index) << DataType::SizeShift(type); source = HeapOperand(obj, offset); } } else { @@ -2999,7 +2999,7 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { if (!needs_write_barrier) { DCHECK(!may_need_runtime_call_for_type_check); if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << DataType::SizeShift(value_type); + offset += Int64FromLocation(index) << DataType::SizeShift(value_type); destination = HeapOperand(array, offset); } else { UseScratchRegisterScope temps(masm); @@ -3037,7 +3037,7 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { UseScratchRegisterScope temps(masm); Register temp = temps.AcquireSameSizeAs(array); if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << DataType::SizeShift(value_type); + offset += Int64FromLocation(index) << DataType::SizeShift(value_type); destination = HeapOperand(array, offset); } else { destination = HeapOperand(temp, @@ -3347,7 +3347,7 @@ FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) #undef FOR_EACH_CONDITION_INSTRUCTION void InstructionCodeGeneratorARM64::GenerateIntDivForPower2Denom(HDiv* instruction) { - int64_t imm = Int64ConstantFrom(instruction->GetLocations()->InAt(1)); + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm)); DCHECK(IsPowerOfTwo(abs_imm)) << abs_imm; @@ -3426,7 +3426,7 @@ void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperati } void InstructionCodeGeneratorARM64::GenerateIntDivForConstDenom(HDiv *instruction) { - int64_t imm = Int64ConstantFrom(instruction->GetLocations()->InAt(1)); + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); if (imm == 0) { // Do not generate anything. DivZeroCheck would prevent any code to be executed. @@ -3516,7 +3516,7 @@ void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction } if (value.IsConstant()) { - int64_t divisor = Int64ConstantFrom(value); + int64_t divisor = Int64FromLocation(value); if (divisor == 0) { __ B(slow_path->GetEntryLabel()); } else { @@ -5636,7 +5636,7 @@ void LocationsBuilderARM64::VisitRem(HRem* rem) { } void InstructionCodeGeneratorARM64::GenerateIntRemForPower2Denom(HRem *instruction) { - int64_t imm = Int64ConstantFrom(instruction->GetLocations()->InAt(1)); + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm)); DCHECK(IsPowerOfTwo(abs_imm)) << abs_imm; @@ -5659,7 +5659,7 @@ void InstructionCodeGeneratorARM64::GenerateIntRemForPower2Denom(HRem *instructi } void InstructionCodeGeneratorARM64::GenerateIntRemForOneOrMinusOneDenom(HRem *instruction) { - int64_t imm = Int64ConstantFrom(instruction->GetLocations()->InAt(1)); + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); DCHECK(imm == 1 || imm == -1) << imm; Register out = OutputRegister(instruction); @@ -5667,7 +5667,7 @@ void InstructionCodeGeneratorARM64::GenerateIntRemForOneOrMinusOneDenom(HRem *in } void InstructionCodeGeneratorARM64::GenerateIntRemForConstDenom(HRem *instruction) { - int64_t imm = Int64ConstantFrom(instruction->GetLocations()->InAt(1)); + int64_t imm = Int64FromLocation(instruction->GetLocations()->InAt(1)); if (imm == 0) { // Do not generate anything. @@ -6666,7 +6666,7 @@ void CodeGeneratorARM64::GenerateRawReferenceLoad(HInstruction* instruction, // ArrayGet and UnsafeGetObject and UnsafeCASObject intrinsics cases. // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor)) if (index.IsConstant()) { - uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor); + uint32_t computed_offset = offset + (Int64FromLocation(index) << scale_factor); EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); Load(type, ref_reg, HeapOperand(obj, computed_offset)); if (needs_null_check) { diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index 6b0ec253e9..6d135a9bfb 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -29,7 +29,7 @@ using helpers::Arm64CanEncodeConstantAsImmediate; using helpers::DRegisterFrom; using helpers::HeapOperand; using helpers::InputRegisterAt; -using helpers::Int64ConstantFrom; +using helpers::Int64FromLocation; using helpers::OutputRegister; using helpers::VRegisterFrom; using helpers::WRegisterFrom; @@ -78,7 +78,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt8: DCHECK_EQ(16u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V16B(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V16B(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V16B(), InputRegisterAt(instruction, 0)); } @@ -87,7 +87,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt16: DCHECK_EQ(8u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V8H(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V8H(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V8H(), InputRegisterAt(instruction, 0)); } @@ -95,7 +95,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt32: DCHECK_EQ(4u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V4S(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V4S(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V4S(), InputRegisterAt(instruction, 0)); } @@ -103,7 +103,7 @@ void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* case DataType::Type::kInt64: DCHECK_EQ(2u, instruction->GetVectorLength()); if (src_loc.IsConstant()) { - __ Movi(dst.V2D(), Int64ConstantFrom(src_loc)); + __ Movi(dst.V2D(), Int64FromLocation(src_loc)); } else { __ Dup(dst.V2D(), XRegisterFrom(src_loc)); } @@ -1333,7 +1333,7 @@ MemOperand InstructionCodeGeneratorARM64::VecAddress( DCHECK(!instruction->InputAt(0)->IsIntermediateAddress()); if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << shift; + offset += Int64FromLocation(index) << shift; return HeapOperand(base, offset); } else { *scratch = temps_scope->AcquireSameSizeAs(base); diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index 5191ee2b1e..5556f16740 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -151,23 +151,15 @@ inline vixl::aarch64::CPURegister InputCPURegisterOrZeroRegAt(HInstruction* inst return InputCPURegisterAt(instr, index); } -inline int64_t Int64ConstantFrom(Location location) { - HConstant* instr = location.GetConstant(); - if (instr->IsIntConstant()) { - return instr->AsIntConstant()->GetValue(); - } else if (instr->IsNullConstant()) { - return 0; - } else { - DCHECK(instr->IsLongConstant()) << instr->DebugName(); - return instr->AsLongConstant()->GetValue(); - } +inline int64_t Int64FromLocation(Location location) { + return Int64FromConstant(location.GetConstant()); } inline vixl::aarch64::Operand OperandFrom(Location location, DataType::Type type) { if (location.IsRegister()) { return vixl::aarch64::Operand(RegisterFrom(location, type)); } else { - return vixl::aarch64::Operand(Int64ConstantFrom(location)); + return vixl::aarch64::Operand(Int64FromLocation(location)); } } diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 85eaa844d2..7aa1ebb98e 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -425,8 +425,8 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(java_lang_dex_file_ != nullptr); const DexFile& dex_file = *java_lang_dex_file_; uint32_t dex_file_checksum = dex_file.GetLocationChecksum(); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), - &dex_file_checksum); + const OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), + &dex_file_checksum); ASSERT_TRUE(oat_dex_file != nullptr); CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); ScopedObjectAccess soa(Thread::Current()); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 2b0095ce27..21ce8c84c4 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -115,10 +115,9 @@ const char* image_roots_descriptions_[] = { }; // Map is so that we don't allocate multiple dex files for the same OatDexFile. -static std::map<const OatFile::OatDexFile*, - std::unique_ptr<const DexFile>> opened_dex_files; +static std::map<const OatDexFile*, std::unique_ptr<const DexFile>> opened_dex_files; -const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* error_msg) { +const DexFile* OpenDexFile(const OatDexFile* oat_dex_file, std::string* error_msg) { DCHECK(oat_dex_file != nullptr); auto it = opened_dex_files.find(oat_dex_file); if (it != opened_dex_files.end()) { @@ -240,15 +239,15 @@ class OatSymbolizer FINAL { } void Walk() { - std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles(); + std::vector<const OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles(); for (size_t i = 0; i < oat_dex_files.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i]; + const OatDexFile* oat_dex_file = oat_dex_files[i]; CHECK(oat_dex_file != nullptr); WalkOatDexFile(oat_dex_file); } } - void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file) { + void WalkOatDexFile(const OatDexFile* oat_dex_file) { std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); if (dex_file == nullptr) { @@ -529,7 +528,7 @@ class OatDumper { // Dumping the dex file overview is compact enough to do even if header only. for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); @@ -598,7 +597,7 @@ class OatDumper { << "\n"; } for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); if (!DumpOatDexFile(os, *oat_dex_file)) { success = false; @@ -630,7 +629,7 @@ class OatDumper { size_t i = 0; for (const auto& vdex_dex_file : vdex_dex_files) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); CHECK(vdex_dex_file != nullptr); if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) { @@ -670,7 +669,7 @@ class OatDumper { const void* GetQuickOatCode(ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) { for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); @@ -786,7 +785,7 @@ class OatDumper { // region, so if we keep a sorted sequence of the start of each region, we can infer the length // of a piece of code by using upper_bound to find the start of the next region. for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + const OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); @@ -825,7 +824,7 @@ class OatDumper { offsets_.insert(oat_method.GetVmapTableOffset()); } - bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { + bool DumpOatDexFile(std::ostream& os, const OatDexFile& oat_dex_file) { bool success = true; bool stop_analysis = false; os << "OatDexFile:\n"; @@ -906,9 +905,7 @@ class OatDumper { // Dex resource is extracted from the oat_dex_file and its checksum is repaired since it's not // unquickened. Otherwise the dex_file has been fully unquickened and is expected to verify the // original checksum. - bool ExportDexFile(std::ostream& os, - const OatFile::OatDexFile& oat_dex_file, - const DexFile* dex_file) { + bool ExportDexFile(std::ostream& os, const OatDexFile& oat_dex_file, const DexFile* dex_file) { std::string error_msg; std::string dex_file_location = oat_dex_file.GetDexFileLocation(); size_t fsize = oat_dex_file.FileSize(); @@ -1717,7 +1714,7 @@ class OatDumper { } const OatFile& oat_file_; - const std::vector<const OatFile::OatDexFile*> oat_dex_files_; + const std::vector<const OatDexFile*> oat_dex_files_; const OatDumperOptions& options_; uint32_t resolved_addr2instr_; const InstructionSet instruction_set_; @@ -1856,7 +1853,7 @@ class ImageDumper { oat_dumper_.reset(new OatDumper(*oat_file, *oat_dumper_options_)); - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { CHECK(oat_dex_file != nullptr); stats_.oat_dex_file_sizes.push_back(std::make_pair(oat_dex_file->GetDexFileLocation(), oat_dex_file->FileSize())); @@ -2782,7 +2779,7 @@ static jobject InstallOatFile(Runtime* runtime, OatFile* oat_file_ptr = oat_file.get(); ClassLinker* class_linker = runtime->GetClassLinker(); runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file)); - for (const OatFile::OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) { + for (const OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) { std::string error_msg; const DexFile* const dex_file = OpenDexFile(odf, &error_msg); CHECK(dex_file != nullptr) << error_msg; diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 5b4dcb73fd..80b6921c8a 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -550,7 +550,7 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() { const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { return ArrayRef<const uint8_t>(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 7b92ba41a5..c219d3dd68 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1338,7 +1338,7 @@ static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file, REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(error_msg != nullptr); std::unique_ptr<const DexFile> dex_file; - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr, error_msg); + const OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr, error_msg); if (oat_dex_file == nullptr) { return std::unique_ptr<const DexFile>(); } @@ -4181,7 +4181,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, } } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); // In case we run without an image there won't be a backing oat file. if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { return false; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index e754fbcbae..cbce940337 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1875,7 +1875,7 @@ std::string ImageSpace::GetMultiImageBootClassPath( bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) { const ArtDexFileLoader dex_file_loader; - for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { + for (const OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); // Skip multidex locations - These will be checked when we visit their @@ -1909,9 +1909,9 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg std::string multi_dex_location = DexFileLoader::GetMultiDexLocation( i, dex_file_location.c_str()); - const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(), - nullptr, - error_msg); + const OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(), + nullptr, + error_msg); if (multi_dex == nullptr) { *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'", oat_file.GetLocation().c_str(), diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 9355ae720d..8842942e7a 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1598,9 +1598,9 @@ ArrayRef<GcRoot<mirror::Object>> OatFile::GetBssGcRoots() const { } } -const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, - const uint32_t* dex_location_checksum, - std::string* error_msg) const { +const OatDexFile* OatFile::GetOatDexFile(const char* dex_location, + const uint32_t* dex_location_checksum, + std::string* error_msg) const { // NOTE: We assume here that the canonical location for a given dex_location never // changes. If it does (i.e. some symlink used by the filename changes) we may return // an incorrect OatDexFile. As long as we have a checksum to check, we shall return @@ -1609,7 +1609,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, // TODO: Additional analysis of usage patterns to see if this can be simplified // without any performance loss, for example by not doing the first lock-free lookup. - const OatFile::OatDexFile* oat_dex_file = nullptr; + const OatDexFile* oat_dex_file = nullptr; StringPiece key(dex_location); // Try to find the key cheaply in the oat_dex_files_ map which holds dex locations // directly mentioned in the oat file and doesn't require locking. @@ -1667,17 +1667,17 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, return oat_dex_file; } -OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, - const std::string& dex_file_location, - const std::string& canonical_dex_file_location, - uint32_t dex_file_location_checksum, - const uint8_t* dex_file_pointer, - const uint8_t* lookup_table_data, - const IndexBssMapping* method_bss_mapping_data, - const IndexBssMapping* type_bss_mapping_data, - const IndexBssMapping* string_bss_mapping_data, - const uint32_t* oat_class_offsets_pointer, - const DexLayoutSections* dex_layout_sections) +OatDexFile::OatDexFile(const OatFile* oat_file, + const std::string& dex_file_location, + const std::string& canonical_dex_file_location, + uint32_t dex_file_location_checksum, + const uint8_t* dex_file_pointer, + const uint8_t* lookup_table_data, + const IndexBssMapping* method_bss_mapping_data, + const IndexBssMapping* type_bss_mapping_data, + const IndexBssMapping* string_bss_mapping_data, + const uint32_t* oat_class_offsets_pointer, + const DexLayoutSections* dex_layout_sections) : oat_file_(oat_file), dex_file_location_(dex_file_location), canonical_dex_file_location_(canonical_dex_file_location), @@ -1708,16 +1708,15 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, } } -OatFile::OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) - : lookup_table_(std::move(lookup_table)) {} +OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {} -OatFile::OatDexFile::~OatDexFile() {} +OatDexFile::~OatDexFile() {} -size_t OatFile::OatDexFile::FileSize() const { +size_t OatDexFile::FileSize() const { return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_; } -std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { +std::unique_ptr<const DexFile> OatDexFile::OpenDexFile(std::string* error_msg) const { ScopedTrace trace(__PRETTY_FUNCTION__); static constexpr bool kVerify = false; static constexpr bool kVerifyChecksum = false; @@ -1732,11 +1731,11 @@ std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* err error_msg); } -uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { +uint32_t OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { return oat_class_offsets_pointer_[class_def_index]; } -OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const { +OatFile::OatClass OatDexFile::GetOatClass(uint16_t class_def_index) const { uint32_t oat_class_offset = GetOatClassOffset(class_def_index); const uint8_t* oat_class_pointer = oat_file_->Begin() + oat_class_offset; @@ -1778,10 +1777,10 @@ OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) con reinterpret_cast<const OatMethodOffsets*>(methods_pointer)); } -const DexFile::ClassDef* OatFile::OatDexFile::FindClassDef(const DexFile& dex_file, - const char* descriptor, - size_t hash) { - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); +const DexFile::ClassDef* OatDexFile::FindClassDef(const DexFile& dex_file, + const char* descriptor, + size_t hash) { + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash); bool used_lookup_table = false; const DexFile::ClassDef* lookup_table_classdef = nullptr; @@ -1829,7 +1828,7 @@ void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) { dex_file.Begin() + dex_file.Size(), MADV_RANDOM); } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file != nullptr) { // Should always be there. const DexLayoutSections* const sections = oat_dex_file->GetDexLayoutSections(); @@ -1947,7 +1946,7 @@ OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) { DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { *found = false; return OatFile::OatClass::Invalid(); @@ -1956,7 +1955,7 @@ OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, return oat_dex_file->GetOatClass(class_def_idx); } -void OatFile::OatDexFile::AssertAotCompiler() { +void OatDexFile::AssertAotCompiler() { CHECK(Runtime::Current()->IsAotCompiler()); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index d72b6a8971..5f87bf0f99 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -70,8 +70,6 @@ class OatFile { // Special classpath that skips shared library check. static constexpr const char* kSpecialSharedLibrary = "&"; - typedef art::OatDexFile OatDexFile; - // Opens an oat file contained within the given elf file. This is always opened as // non-executable at the moment. static OatFile* OpenWithElfFile(int zip_fd, diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 6c869cada5..f7c74cc23b 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -354,7 +354,7 @@ bool OatFileAssistant::LoadDexFiles( std::vector<std::unique_ptr<const DexFile>>* out_dex_files) { // Load the main dex file. std::string error_msg; - const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile( + const OatDexFile* oat_dex_file = oat_file.GetOatDexFile( dex_location.c_str(), nullptr, &error_msg); if (oat_dex_file == nullptr) { LOG(WARNING) << error_msg; @@ -453,7 +453,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* err for (uint32_t i = 0; i < number_of_dex_files; i++) { std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str()); uint32_t expected_checksum = (*required_dex_checksums)[i]; - const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr); + const OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr); if (oat_dex_file == nullptr) { *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str()); return false; @@ -921,7 +921,7 @@ const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { required_dex_checksums_found_ = true; for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) { std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str()); - const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr); + const OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr); if (odex_dex_file == nullptr) { required_dex_checksums_found_ = false; break; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 3b4d177646..f7674c89d3 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1020,7 +1020,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location, return false; } - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { if (oat_dex_file == nullptr) { *failures += 1; continue; diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index 7c382588a4..a8a895a6b3 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -28,7 +28,7 @@ namespace art { class NoPatchoatTest { public: - static const OatFile::OatDexFile* getOatDexFile(jclass cls) { + static const OatDexFile* getOatDexFile(jclass cls) { ScopedObjectAccess soa(Thread::Current()); ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); const DexFile& dex_file = klass->GetDexFile(); @@ -42,13 +42,13 @@ class NoPatchoatTest { } static bool hasExecutableOat(jclass cls) { - const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls); + const OatDexFile* oat_dex_file = getOatDexFile(cls); return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable(); } static bool needsRelocation(jclass cls) { - const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls); + const OatDexFile* oat_dex_file = getOatDexFile(cls); if (oat_dex_file == nullptr) { return false; diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index f89888bb99..f2da6febe0 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -60,7 +60,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasOatFile(JNIEnv* env, jclass c ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); const DexFile& dex_file = klass->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); return (oat_dex_file != nullptr) ? JNI_TRUE : JNI_FALSE; } @@ -100,7 +100,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* e ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); const DexFile& dex_file = klass->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr) { // Could be JIT, which also uses optimizing, but conservatively say no. return JNI_FALSE; diff --git a/test/knownfailures.json b/test/knownfailures.json index f6ae0be5c0..9ba2b50cba 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -454,6 +454,14 @@ }, { "tests": [ + "638-no-line-number" + ], + "description": ["Tests that fail on redefine stress due to branch instruction selection"], + "bug": "b/110869946", + "variant": "redefine-stress" + }, + { + "tests": [ "097-duplicate-method", "138-duplicate-classes-check2", "159-app-image-fields", diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt index 84266d9164..f60c1a84fa 100644 --- a/tools/ahat/etc/ahat_api.txt +++ b/tools/ahat/etc/ahat_api.txt @@ -66,19 +66,21 @@ package com.android.ahat.heapdump { method public java.util.List<com.android.ahat.heapdump.AhatInstance> getDominated(); method public java.lang.Object getDominatorsComputationState(); method public com.android.ahat.heapdump.Value getField(java.lang.String); - method public java.util.List<com.android.ahat.heapdump.AhatInstance> getHardReverseReferences(); + method public deprecated java.util.List<com.android.ahat.heapdump.AhatInstance> getHardReverseReferences(); method public com.android.ahat.heapdump.AhatHeap getHeap(); method public long getId(); method public com.android.ahat.heapdump.AhatInstance getImmediateDominator(); method public java.util.List<com.android.ahat.heapdump.PathElement> getPathFromGcRoot(); + method public com.android.ahat.heapdump.Reachability getReachability(); method public com.android.ahat.heapdump.AhatInstance getRefField(java.lang.String); method public java.lang.Iterable<? extends com.android.ahat.dominators.DominatorsComputation.Node> getReferencesForDominators(); method public com.android.ahat.heapdump.AhatInstance getReferent(); method public com.android.ahat.heapdump.Size getRetainedSize(com.android.ahat.heapdump.AhatHeap); + method public java.util.List<com.android.ahat.heapdump.AhatInstance> getReverseReferences(); method public java.util.Collection<com.android.ahat.heapdump.RootType> getRootTypes(); method public com.android.ahat.heapdump.Site getSite(); method public com.android.ahat.heapdump.Size getSize(); - method public java.util.List<com.android.ahat.heapdump.AhatInstance> getSoftReverseReferences(); + method public deprecated java.util.List<com.android.ahat.heapdump.AhatInstance> getSoftReverseReferences(); method public com.android.ahat.heapdump.Size getTotalRetainedSize(); method public boolean isArrayInstance(); method public boolean isClassInstance(); @@ -87,7 +89,7 @@ package com.android.ahat.heapdump { method public boolean isRoot(); method public boolean isStronglyReachable(); method public boolean isUnreachable(); - method public boolean isWeaklyReachable(); + method public deprecated boolean isWeaklyReachable(); method public void setDominator(com.android.ahat.dominators.DominatorsComputation.Node); method public void setDominatorsComputationState(java.lang.Object); method public abstract java.lang.String toString(); @@ -174,6 +176,17 @@ package com.android.ahat.heapdump { field public boolean isDominator; } + public final class Reachability extends java.lang.Enum { + method public static com.android.ahat.heapdump.Reachability valueOf(java.lang.String); + method public static final com.android.ahat.heapdump.Reachability[] values(); + enum_constant public static final com.android.ahat.heapdump.Reachability FINALIZER; + enum_constant public static final com.android.ahat.heapdump.Reachability PHANTOM; + enum_constant public static final com.android.ahat.heapdump.Reachability SOFT; + enum_constant public static final com.android.ahat.heapdump.Reachability STRONG; + enum_constant public static final com.android.ahat.heapdump.Reachability UNREACHABLE; + enum_constant public static final com.android.ahat.heapdump.Reachability WEAK; + } + public final class RootType extends java.lang.Enum { method public static com.android.ahat.heapdump.RootType valueOf(java.lang.String); method public static final com.android.ahat.heapdump.RootType[] values(); diff --git a/tools/ahat/src/main/com/android/ahat/ObjectHandler.java b/tools/ahat/src/main/com/android/ahat/ObjectHandler.java index bfd5d5cacd..c099da8ceb 100644 --- a/tools/ahat/src/main/com/android/ahat/ObjectHandler.java +++ b/tools/ahat/src/main/com/android/ahat/ObjectHandler.java @@ -44,8 +44,7 @@ class ObjectHandler implements AhatHandler { private static final String DOMINATED_OBJECTS_ID = "dominated"; private static final String INSTANCE_FIELDS_ID = "ifields"; private static final String STATIC_FIELDS_ID = "sfields"; - private static final String HARD_REFS_ID = "refs"; - private static final String SOFT_REFS_ID = "srefs"; + private static final String REFS_ID = "refs"; private AhatSnapshot mSnapshot; @@ -223,24 +222,12 @@ class ObjectHandler implements AhatHandler { private static void printReferences(Doc doc, Query query, AhatInstance inst) { doc.section("Objects with References to this Object"); - if (inst.getHardReverseReferences().isEmpty()) { + if (inst.getReverseReferences().isEmpty()) { doc.println(DocString.text("(none)")); } else { doc.table(new Column("Object")); - List<AhatInstance> references = inst.getHardReverseReferences(); - SubsetSelector<AhatInstance> selector = new SubsetSelector(query, HARD_REFS_ID, references); - for (AhatInstance ref : selector.selected()) { - doc.row(Summarizer.summarize(ref)); - } - doc.end(); - selector.render(doc); - } - - if (!inst.getSoftReverseReferences().isEmpty()) { - doc.section("Objects with Soft References to this Object"); - doc.table(new Column("Object")); - List<AhatInstance> references = inst.getSoftReverseReferences(); - SubsetSelector<AhatInstance> selector = new SubsetSelector(query, SOFT_REFS_ID, references); + List<AhatInstance> references = inst.getReverseReferences(); + SubsetSelector<AhatInstance> selector = new SubsetSelector(query, REFS_ID, references); for (AhatInstance ref : selector.selected()) { doc.row(Summarizer.summarize(ref)); } diff --git a/tools/ahat/src/main/com/android/ahat/Summarizer.java b/tools/ahat/src/main/com/android/ahat/Summarizer.java index 127ff37aea..ab88c04d32 100644 --- a/tools/ahat/src/main/com/android/ahat/Summarizer.java +++ b/tools/ahat/src/main/com/android/ahat/Summarizer.java @@ -18,6 +18,7 @@ package com.android.ahat; import com.android.ahat.heapdump.AhatClassObj; import com.android.ahat.heapdump.AhatInstance; +import com.android.ahat.heapdump.Reachability; import com.android.ahat.heapdump.Site; import com.android.ahat.heapdump.Value; import java.net.URI; @@ -51,11 +52,10 @@ class Summarizer { formatted.append(DocString.removed("del ")); } - // Annotate unreachable objects as such. - if (inst.isWeaklyReachable()) { - formatted.append("weak "); - } else if (inst.isUnreachable()) { - formatted.append("unreachable "); + // Annotate non-strongly reachable objects as such. + Reachability reachability = inst.getReachability(); + if (reachability != Reachability.STRONG) { + formatted.append(reachability.toString() + " "); } // Annotate roots as roots. diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java index c574e98788..cf48d6d459 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java @@ -240,7 +240,10 @@ public class AhatArrayInstance extends AhatInstance { if (value != null) { assert value.isAhatInstance(); String field = "[" + Integer.toString(index) + "]"; - return new Reference(AhatArrayInstance.this, field, value.asAhatInstance(), true); + return new Reference(AhatArrayInstance.this, + field, + value.asAhatInstance(), + Reachability.STRONG); } return null; } @@ -324,7 +327,7 @@ public class AhatArrayInstance extends AhatInstance { @Override public AhatInstance getAssociatedBitmapInstance() { if (mByteArray != null) { - List<AhatInstance> refs = getHardReverseReferences(); + List<AhatInstance> refs = getReverseReferences(); if (refs.size() == 1) { AhatInstance ref = refs.get(0); return ref.getAssociatedBitmapInstance(); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java index c82ef20e9b..f377ae37bc 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java @@ -104,10 +104,7 @@ public class AhatClassInstance extends AhatInstance { @Override Iterable<Reference> getReferences() { - if (isInstanceOfClass("java.lang.ref.Reference")) { - return new WeakReferentReferenceIterator(); - } - return new StrongReferenceIterator(); + return new ReferenceIterator(); } /** @@ -352,59 +349,48 @@ public class AhatClassInstance extends AhatInstance { } /** - * A Reference iterator that iterates over the fields of this instance - * assuming all field references are strong references. + * Returns the reachability type associated with this instance. + * For example, returns Reachability.WEAK for an instance of + * java.lang.ref.WeakReference. */ - private class StrongReferenceIterator implements Iterable<Reference>, - Iterator<Reference> { - private Iterator<FieldValue> mIter = getInstanceFields().iterator(); - private Reference mNext = null; - - @Override - public boolean hasNext() { - while (mNext == null && mIter.hasNext()) { - FieldValue field = mIter.next(); - if (field.value != null && field.value.isAhatInstance()) { - AhatInstance ref = field.value.asAhatInstance(); - mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, true); - } - } - return mNext != null; - } - - @Override - public Reference next() { - if (!hasNext()) { - throw new NoSuchElementException(); + private Reachability getJavaLangRefType() { + AhatClassObj cls = getClassObj(); + while (cls != null) { + switch (cls.getName()) { + case "java.lang.ref.PhantomReference": return Reachability.PHANTOM; + case "java.lang.ref.WeakReference": return Reachability.WEAK; + case "java.lang.ref.FinalizerReference": return Reachability.FINALIZER; + case "java.lang.ref.SoftReference": return Reachability.SOFT; } - Reference next = mNext; - mNext = null; - return next; - } - - @Override - public Iterator<Reference> iterator() { - return this; + cls = cls.getSuperClassObj(); } + return Reachability.STRONG; } /** - * A Reference iterator that iterates over the fields of a subclass of - * java.lang.ref.Reference, where the 'referent' field is considered weak. + * A Reference iterator that iterates over the fields of this instance. */ - private class WeakReferentReferenceIterator implements Iterable<Reference>, - Iterator<Reference> { - private Iterator<FieldValue> mIter = getInstanceFields().iterator(); + private class ReferenceIterator implements Iterable<Reference>, + Iterator<Reference> { + private final Iterator<FieldValue> mIter = getInstanceFields().iterator(); private Reference mNext = null; + // If we are iterating over a subclass of java.lang.ref.Reference, the + // 'referent' field doesn't have strong reachability. mJavaLangRefType + // describes what type of java.lang.ref.Reference subinstance this is. + private final Reachability mJavaLangRefType = getJavaLangRefType(); + @Override public boolean hasNext() { while (mNext == null && mIter.hasNext()) { FieldValue field = mIter.next(); if (field.value != null && field.value.isAhatInstance()) { - boolean strong = !field.name.equals("referent"); + Reachability reachability = Reachability.STRONG; + if (mJavaLangRefType != Reachability.STRONG && "referent".equals(field.name)) { + reachability = mJavaLangRefType; + } AhatInstance ref = field.value.asAhatInstance(); - mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, strong); + mNext = new Reference(AhatClassInstance.this, "." + field.name, ref, reachability); } } return mNext != null; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java index 36ada2857c..765a411e41 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java @@ -131,7 +131,10 @@ public class AhatClassObj extends AhatInstance { FieldValue field = mStaticFieldValues[index]; Value value = field.value; if (value != null && value.isAhatInstance()) { - return new Reference(AhatClassObj.this, "." + field.name, value.asAhatInstance(), true); + return new Reference(AhatClassObj.this, + "." + field.name, + value.asAhatInstance(), + Reachability.STRONG); } return null; } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java index a91da82ce9..20f368f4ff 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Deque; +import java.util.EnumMap; import java.util.List; import java.util.Queue; @@ -48,11 +49,11 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, // Field initialized via addRegisterednativeSize. private long mRegisteredNativeSize = 0; - // Fields initialized in computeReverseReferences(). + // Fields initialized in computeReachability(). + private Reachability mReachability = Reachability.UNREACHABLE; private AhatInstance mNextInstanceToGcRoot; private String mNextInstanceToGcRootField; - private ArrayList<AhatInstance> mHardReverseReferences; - private ArrayList<AhatInstance> mSoftReverseReferences; + private ArrayList<AhatInstance> mReverseReferences; // Fields initialized in DominatorsComputation.computeDominators(). // mDominated - the list of instances immediately dominated by this instance. @@ -157,6 +158,15 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, } /** + * Returns the reachability of the instance. + * + * @return the reachability of the instance. + */ + public Reachability getReachability() { + return mReachability; + } + + /** * Returns true if this object is strongly reachable. An object is strongly * reachable if there exists a path of (strong) references from some root * object to this object. @@ -164,7 +174,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * @return true if the object is strongly reachable */ public boolean isStronglyReachable() { - return mImmediateDominator != null; + return mReachability == Reachability.STRONG; } /** @@ -178,10 +188,13 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * Unlike a strongly reachable object, a weakly reachable object is allowed * to be garbage collected. * + * @deprecated Use {@link #getReachability()} instead, which can distinguish + * among soft, weak, phantom, and other kinds of references. + * * @return true if the object is weakly reachable */ - public boolean isWeaklyReachable() { - return !isStronglyReachable() && mNextInstanceToGcRoot != null; + @Deprecated public boolean isWeaklyReachable() { + return !isStronglyReachable() && !isUnreachable(); } /** @@ -193,7 +206,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * @return true if the object is completely unreachable */ public boolean isUnreachable() { - return !isStronglyReachable() && !isWeaklyReachable(); + return mReachability == Reachability.UNREACHABLE; } /** @@ -215,7 +228,6 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, * Returns true if this instance is a GC root. * * @return true if this instance is a GC root. - * @see getRootTypes */ public boolean isRoot() { return mRootTypes != 0; @@ -374,28 +386,50 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, } /** - * Returns a list of objects with (strong) references to this object. + * Returns a list of objects with any kind of reference to this object. * * @return the objects referencing this object */ - public List<AhatInstance> getHardReverseReferences() { - if (mHardReverseReferences != null) { - return mHardReverseReferences; + public List<AhatInstance> getReverseReferences() { + if (mReverseReferences != null) { + return mReverseReferences; } return Collections.emptyList(); } /** + * Returns a list of objects with (strong) references to this object. + * + * @deprecated Use {@link #getReverseReferences()} instead. + * + * @return the objects referencing this object + */ + @Deprecated public List<AhatInstance> getHardReverseReferences() { + List<AhatInstance> refs = new ArrayList<AhatInstance>(); + for (AhatInstance ref : getReverseReferences()) { + if (ref.getReachability() == Reachability.STRONG && ref.getReferent() != this) { + refs.add(ref); + } + } + return refs; + } + + /** * Returns a list of objects with soft/weak/phantom/finalizer references to * this object. * + * @deprecated Use {@link #getReverseReferences()} instead. + * * @return the objects weakly referencing this object */ - public List<AhatInstance> getSoftReverseReferences() { - if (mSoftReverseReferences != null) { - return mSoftReverseReferences; + @Deprecated public List<AhatInstance> getSoftReverseReferences() { + List<AhatInstance> refs = new ArrayList<AhatInstance>(); + for (AhatInstance ref : getReverseReferences()) { + if (ref.getReachability() != Reachability.STRONG || ref.getReferent() == this) { + refs.add(ref); + } } - return Collections.emptyList(); + return refs; } /** @@ -610,82 +644,60 @@ public abstract class AhatInstance implements Diffable<AhatInstance>, } /** - * Initialize the reverse reference fields of this instance and all other - * instances reachable from it. Initializes the following fields: + * Determine the reachability of the all instances reachable from the given + * root instance. Initializes the following fields: + * mReachability * mNextInstanceToGcRoot * mNextInstanceToGcRootField - * mHardReverseReferences - * mSoftReverseReferences + * mReverseReferences * * @param progress used to track progress of the traversal. * @param numInsts upper bound on the total number of instances reachable * from the root, solely used for the purposes of tracking * progress. */ - static void computeReverseReferences(SuperRoot root, Progress progress, long numInsts) { + static void computeReachability(SuperRoot root, Progress progress, long numInsts) { // Start by doing a breadth first search through strong references. - // Then continue the breadth first search through weak references. - progress.start("Reversing references", numInsts); - Queue<Reference> strong = new ArrayDeque<Reference>(); - Queue<Reference> weak = new ArrayDeque<Reference>(); + // Then continue the breadth first through each weaker kind of reference. + progress.start("Computing reachability", numInsts); + EnumMap<Reachability, Queue<Reference>> queues = new EnumMap<>(Reachability.class); + for (Reachability reachability : Reachability.values()) { + queues.put(reachability, new ArrayDeque<Reference>()); + } for (Reference ref : root.getReferences()) { - strong.add(ref); + queues.get(Reachability.STRONG).add(ref); } - while (!strong.isEmpty()) { - Reference ref = strong.poll(); - assert ref.strong; - - if (ref.ref.mNextInstanceToGcRoot == null) { - // This is the first time we have seen ref.ref. - progress.advance(); - ref.ref.mNextInstanceToGcRoot = ref.src; - ref.ref.mNextInstanceToGcRootField = ref.field; - ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>(); - - for (Reference childRef : ref.ref.getReferences()) { - if (childRef.strong) { - strong.add(childRef); - } else { - weak.add(childRef); + for (Reachability reachability : Reachability.values()) { + Queue<Reference> queue = queues.get(reachability); + while (!queue.isEmpty()) { + Reference ref = queue.poll(); + if (ref.ref.mReachability == Reachability.UNREACHABLE) { + // This is the first time we have seen ref.ref. + progress.advance(); + ref.ref.mReachability = reachability; + ref.ref.mNextInstanceToGcRoot = ref.src; + ref.ref.mNextInstanceToGcRootField = ref.field; + ref.ref.mReverseReferences = new ArrayList<AhatInstance>(); + + for (Reference childRef : ref.ref.getReferences()) { + if (childRef.reachability.ordinal() <= reachability.ordinal()) { + queue.add(childRef); + } else { + queues.get(childRef.reachability).add(childRef); + } } } - } - - // Note: We specifically exclude 'root' from the reverse references - // because it is a fake SuperRoot instance not present in the original - // heap dump. - if (ref.src != root) { - ref.ref.mHardReverseReferences.add(ref.src); - } - } - - while (!weak.isEmpty()) { - Reference ref = weak.poll(); - - if (ref.ref.mNextInstanceToGcRoot == null) { - // This is the first time we have seen ref.ref. - progress.advance(); - ref.ref.mNextInstanceToGcRoot = ref.src; - ref.ref.mNextInstanceToGcRootField = ref.field; - ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>(); - - for (Reference childRef : ref.ref.getReferences()) { - weak.add(childRef); - } - } - if (ref.strong) { - ref.ref.mHardReverseReferences.add(ref.src); - } else { - if (ref.ref.mSoftReverseReferences == null) { - ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>(); + // Note: We specifically exclude 'root' from the reverse references + // because it is a fake SuperRoot instance not present in the original + // heap dump. + if (ref.src != root) { + ref.ref.mReverseReferences.add(ref.src); } - ref.ref.mSoftReverseReferences.add(ref.src); } } - progress.done(); } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java index bc940479b1..d9c7a19431 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java @@ -55,7 +55,7 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> { } } - AhatInstance.computeReverseReferences(mSuperRoot, progress, mInstances.size()); + AhatInstance.computeReachability(mSuperRoot, progress, mInstances.size()); DominatorsComputation.computeDominators(mSuperRoot, progress, mInstances.size()); AhatInstance.computeRetainedSize(mSuperRoot, mHeaps.size()); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java b/tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java index 0b99e496cc..8c8de2383b 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java @@ -37,7 +37,7 @@ class DominatorReferenceIterator implements Iterator<AhatInstance>, public boolean hasNext() { while (mNext == null && mIter.hasNext()) { Reference ref = mIter.next(); - if (ref.strong) { + if (ref.reachability == Reachability.STRONG) { mNext = ref.ref; } } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Reachability.java b/tools/ahat/src/main/com/android/ahat/heapdump/Reachability.java new file mode 100644 index 0000000000..8df6c8ca23 --- /dev/null +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Reachability.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.heapdump; + +/** + * Enum corresponding to the reachability of an instance. + * See {@link java.lang.ref} for a specification of the various kinds of + * reachibility. The enum constants are specified in decreasing order of + * strength. + */ +public enum Reachability { + /** + * The instance is strongly reachable. + */ + STRONG("strong"), + + /** + * The instance is softly reachable. + */ + SOFT("soft"), + + /** + * The instance is finalizer reachable, but is neither strongly nor softly + * reachable. + */ + FINALIZER("finalizer"), + + /** + * The instance is weakly reachable. + */ + WEAK("weak"), + + /** + * The instance is phantom reachable. + */ + PHANTOM("phantom"), + + /** + * The instance is unreachable. + */ + UNREACHABLE("unreachable"); + + /** + * The name of the reachibility. + */ + private final String name; + + Reachability(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java b/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java index f1340bd07b..2de76fdc87 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Reference.java @@ -20,19 +20,18 @@ package com.android.ahat.heapdump; * Reference represents a reference from 'src' to 'ref' through 'field'. * Field is a string description for human consumption. This is typically * either "." followed by the field name or an array subscript such as "[4]". - * 'strong' is true if this is a strong reference, false if it is a - * weak/soft/other reference. + * reachability describes whether the reference is strong/soft/weak/etc. */ class Reference { public final AhatInstance src; public final String field; public final AhatInstance ref; - public final boolean strong; + public final Reachability reachability; - public Reference(AhatInstance src, String field, AhatInstance ref, boolean strong) { + public Reference(AhatInstance src, String field, AhatInstance ref, Reachability reachability) { this.src = src; this.field = field; this.ref = ref; - this.strong = strong; + this.reachability = reachability; } } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java b/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java index b01cffff72..d06df900fb 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java @@ -54,7 +54,7 @@ class SuperRoot extends AhatInstance implements DominatorsComputation.Node { @Override public Reference get(int index) { String field = ".roots[" + Integer.toString(index) + "]"; - return new Reference(SuperRoot.this, field, mRoots.get(index), true); + return new Reference(SuperRoot.this, field, mRoots.get(index), Reachability.STRONG); } }; } diff --git a/tools/ahat/src/test-dump/DumpedStuff.java b/tools/ahat/src/test-dump/DumpedStuff.java index 98ead07492..804a3a37d4 100644 --- a/tools/ahat/src/test-dump/DumpedStuff.java +++ b/tools/ahat/src/test-dump/DumpedStuff.java @@ -136,6 +136,7 @@ public class DumpedStuff extends SuperDumpedStuff { public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue); public WeakReference aNullReferentReference = new WeakReference(null, referenceQueue); public SoftReference aSoftReference = new SoftReference(new Object()); + public Reference reachabilityReferenceChain; public byte[] bigArray; public ObjectTree[] gcPathArray = new ObjectTree[]{null, null, new ObjectTree( @@ -145,7 +146,7 @@ public class DumpedStuff extends SuperDumpedStuff { public Reference aLongStrongPathToSamplePathObject; public WeakReference aShortWeakPathToSamplePathObject; public WeakReference aWeakRefToGcRoot = new WeakReference(Main.class); - public SoftReference aWeakChain = new SoftReference(new Reference(new Reference(new Object()))); + public SoftReference aSoftChain = new SoftReference(new Reference(new Reference(new Object()))); public Object[] basicStringRef; public AddedObject addedObject; public UnchangedObject unchangedObject = new UnchangedObject(); @@ -157,4 +158,15 @@ public class DumpedStuff extends SuperDumpedStuff { public int[] modifiedArray; public Object objectAllocatedAtKnownSite; public Object objectAllocatedAtKnownSubSite; + + // Allocate those objects that we need to not be GC'd before taking the heap + // dump. + public void shouldNotGc() { + reachabilityReferenceChain = new Reference( + new SoftReference( + new Reference( + new WeakReference( + new SoftReference( + new PhantomReference(new Object(), referenceQueue)))))); + } } diff --git a/tools/ahat/src/test-dump/Main.java b/tools/ahat/src/test-dump/Main.java index de3674846b..ca18fd8cec 100644 --- a/tools/ahat/src/test-dump/Main.java +++ b/tools/ahat/src/test-dump/Main.java @@ -49,6 +49,8 @@ public class Main { stuff.basicStringRef = new Object[]{stuff.basicString}; } + stuff.shouldNotGc(); + // Take a heap dump that will include that instance of DumpedStuff. System.err.println("Dumping hprof data to " + file); VMDebug.dumpHprofData(file); diff --git a/tools/ahat/src/test/com/android/ahat/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java index 65a3fb8777..f886e9df5f 100644 --- a/tools/ahat/src/test/com/android/ahat/InstanceTest.java +++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java @@ -21,6 +21,7 @@ import com.android.ahat.heapdump.AhatHeap; import com.android.ahat.heapdump.AhatInstance; import com.android.ahat.heapdump.AhatSnapshot; import com.android.ahat.heapdump.PathElement; +import com.android.ahat.heapdump.Reachability; import com.android.ahat.heapdump.Size; import com.android.ahat.heapdump.Value; import java.io.IOException; @@ -216,10 +217,31 @@ public class InstanceTest { AhatInstance ref = dump.getDumpedAhatInstance("aSoftReference"); AhatInstance referent = ref.getReferent(); assertNotNull(referent); + assertEquals(Reachability.SOFT, referent.getReachability()); assertTrue(referent.isWeaklyReachable()); } @Test + public void reachability() throws IOException { + TestDump dump = TestDump.getTestDump(); + AhatInstance strong1 = dump.getDumpedAhatInstance("reachabilityReferenceChain"); + AhatInstance soft1 = strong1.getField("referent").asAhatInstance(); + AhatInstance strong2 = soft1.getField("referent").asAhatInstance(); + AhatInstance weak1 = strong2.getField("referent").asAhatInstance(); + AhatInstance soft2 = weak1.getField("referent").asAhatInstance(); + AhatInstance phantom1 = soft2.getField("referent").asAhatInstance(); + AhatInstance obj = phantom1.getField("referent").asAhatInstance(); + + assertEquals(Reachability.STRONG, strong1.getReachability()); + assertEquals(Reachability.STRONG, soft1.getReachability()); + assertEquals(Reachability.SOFT, strong2.getReachability()); + assertEquals(Reachability.SOFT, weak1.getReachability()); + assertEquals(Reachability.WEAK, soft2.getReachability()); + assertEquals(Reachability.WEAK, phantom1.getReachability()); + assertEquals(Reachability.PHANTOM, obj.getReachability()); + } + + @Test public void gcRootPath() throws IOException { TestDump dump = TestDump.getTestDump(); @@ -388,24 +410,31 @@ public class InstanceTest { // We had a bug in the past where weak references to GC roots caused the // roots to be incorrectly be considered weakly reachable. + assertEquals(Reachability.STRONG, root.getReachability()); assertTrue(root.isStronglyReachable()); assertFalse(root.isWeaklyReachable()); } @Test - public void weakReferenceChain() throws IOException { + public void softReferenceChain() throws IOException { // If the only reference to a chain of strongly referenced objects is a - // weak reference, then all of the objects should be considered weakly + // soft reference, then all of the objects should be considered softly // reachable. TestDump dump = TestDump.getTestDump(); - AhatInstance ref = dump.getDumpedAhatInstance("aWeakChain"); - AhatInstance weak1 = ref.getField("referent").asAhatInstance(); - AhatInstance weak2 = weak1.getField("referent").asAhatInstance(); - AhatInstance weak3 = weak2.getField("referent").asAhatInstance(); + AhatInstance ref = dump.getDumpedAhatInstance("aSoftChain"); + AhatInstance soft1 = ref.getField("referent").asAhatInstance(); + AhatInstance soft2 = soft1.getField("referent").asAhatInstance(); + AhatInstance soft3 = soft2.getField("referent").asAhatInstance(); assertTrue(ref.isStronglyReachable()); - assertTrue(weak1.isWeaklyReachable()); - assertTrue(weak2.isWeaklyReachable()); - assertTrue(weak3.isWeaklyReachable()); + assertEquals(Reachability.SOFT, soft1.getReachability()); + assertEquals(Reachability.SOFT, soft2.getReachability()); + assertEquals(Reachability.SOFT, soft3.getReachability()); + + // Test the deprecated isWeaklyReachable API, which interprets weak as any + // kind of phantom/finalizer/weak/soft reference. + assertTrue(soft1.isWeaklyReachable()); + assertTrue(soft2.isWeaklyReachable()); + assertTrue(soft3.isWeaklyReachable()); } @Test @@ -414,6 +443,8 @@ public class InstanceTest { AhatInstance obj = dump.getDumpedAhatInstance("anObject"); AhatInstance ref = dump.getDumpedAhatInstance("aReference"); AhatInstance weak = dump.getDumpedAhatInstance("aWeakReference"); + assertTrue(obj.getReverseReferences().contains(ref)); + assertTrue(obj.getReverseReferences().contains(weak)); assertTrue(obj.getHardReverseReferences().contains(ref)); assertFalse(obj.getHardReverseReferences().contains(weak)); assertFalse(obj.getSoftReverseReferences().contains(ref)); |