summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator_arm64.cc30
-rw-r--r--compiler/optimizing/code_generator_vector_arm64.cc12
-rw-r--r--compiler/optimizing/common_arm64.h14
-rw-r--r--dex2oat/linker/oat_writer_test.cc4
-rw-r--r--oatdump/oatdump.cc33
-rw-r--r--runtime/art_method.cc2
-rw-r--r--runtime/class_linker.cc4
-rw-r--r--runtime/gc/space/image_space.cc8
-rw-r--r--runtime/oat_file.cc57
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/oat_file_assistant.cc6
-rw-r--r--runtime/runtime.cc2
-rw-r--r--test/117-nopatchoat/nopatchoat.cc6
-rw-r--r--test/common/runtime_state.cc4
-rw-r--r--test/knownfailures.json8
-rw-r--r--tools/ahat/etc/ahat_api.txt19
-rw-r--r--tools/ahat/src/main/com/android/ahat/ObjectHandler.java21
-rw-r--r--tools/ahat/src/main/com/android/ahat/Summarizer.java10
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java7
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java70
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java5
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java158
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java2
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/DominatorReferenceIterator.java2
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Reachability.java70
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/Reference.java9
-rw-r--r--tools/ahat/src/main/com/android/ahat/heapdump/SuperRoot.java2
-rw-r--r--tools/ahat/src/test-dump/DumpedStuff.java14
-rw-r--r--tools/ahat/src/test-dump/Main.java2
-rw-r--r--tools/ahat/src/test/com/android/ahat/InstanceTest.java49
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));