Add custom SIGSEGV handler to help find heap corruption.
The new signal handler prints heap diagnostics when you get a SIGSEGV.
Added a fault message member in runtime which is modifiable by
Runtime::SetFaultMessage. When you get a SIGSEGV it will print out
whatever is stored in this string as well as the normal information.
This is useful for debugging heap corruption since it lets you see
which threads were in which methods when the last GC occured.
Added some smarter object dumping logic when the faulting address is
in the heap.
Bug: 12934910
Change-Id: Ia72be2c39f70ad711cbd746d66fad2b617d5d29f
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 2e6d2c2..9ad21cf 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -318,6 +318,91 @@
}
}
+std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) {
+ if (!IsValidContinuousSpaceObjectAddress(klass)) {
+ return StringPrintf("<non heap address klass %p>", klass);
+ }
+ mirror::Class* component_type = klass->GetComponentType<kVerifyNone>();
+ if (IsValidContinuousSpaceObjectAddress(component_type) && klass->IsArrayClass<kVerifyNone>()) {
+ std::string result("[");
+ result += SafeGetClassDescriptor(component_type);
+ return result;
+ } else if (UNLIKELY(klass->IsPrimitive<kVerifyNone>())) {
+ return Primitive::Descriptor(klass->GetPrimitiveType<kVerifyNone>());
+ } else if (UNLIKELY(klass->IsProxyClass())) {
+ return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(klass);
+ } else {
+ mirror::DexCache* dex_cache = klass->GetDexCache();
+ if (!IsValidContinuousSpaceObjectAddress(dex_cache)) {
+ return StringPrintf("<non heap address dex_cache %p>", dex_cache);
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ uint16_t class_def_idx = klass->GetDexClassDefIndex();
+ if (class_def_idx == DexFile::kDexNoIndex16) {
+ return "<class def not found>";
+ }
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
+ const DexFile::TypeId& type_id = dex_file->GetTypeId(class_def.class_idx_);
+ return dex_file->GetTypeDescriptor(type_id);
+ }
+}
+
+std::string Heap::SafePrettyTypeOf(mirror::Object* obj) {
+ if (obj == nullptr) {
+ return "null";
+ }
+ mirror::Class* klass = obj->GetClass<kVerifyNone>();
+ if (klass == nullptr) {
+ return "(class=null)";
+ }
+ std::string result(SafeGetClassDescriptor(klass));
+ if (obj->IsClass()) {
+ result += "<" + SafeGetClassDescriptor(obj->AsClass()) + ">";
+ }
+ return result;
+}
+
+void Heap::DumpObject(std::ostream& stream, mirror::Object* obj) {
+ if (obj == nullptr) {
+ stream << "(obj=null)";
+ return;
+ }
+ if (IsAligned<kObjectAlignment>(obj)) {
+ space::Space* space = nullptr;
+ // Don't use find space since it only finds spaces which actually contain objects instead of
+ // spaces which may contain objects (e.g. cleared bump pointer spaces).
+ for (const auto& cur_space : continuous_spaces_) {
+ if (cur_space->HasAddress(obj)) {
+ space = cur_space;
+ break;
+ }
+ }
+ if (space == nullptr) {
+ if (allocator_mem_map_.get() == nullptr || !allocator_mem_map_->HasAddress(obj)) {
+ stream << "obj " << obj << " not a valid heap address";
+ return;
+ } else if (allocator_mem_map_.get() != nullptr) {
+ allocator_mem_map_->Protect(PROT_READ | PROT_WRITE);
+ }
+ }
+ // Unprotect all the spaces.
+ for (const auto& space : continuous_spaces_) {
+ mprotect(space->Begin(), space->Capacity(), PROT_READ | PROT_WRITE);
+ }
+ stream << "Object " << obj;
+ if (space != nullptr) {
+ stream << " in space " << *space;
+ }
+ mirror::Class* klass = obj->GetClass();
+ stream << "\nclass=" << klass;
+ if (klass != nullptr) {
+ stream << " type= " << SafePrettyTypeOf(obj);
+ }
+ // Re-protect the address we faulted on.
+ mprotect(AlignDown(obj, kPageSize), kPageSize, PROT_NONE);
+ }
+}
+
bool Heap::IsCompilingBoot() const {
for (const auto& space : continuous_spaces_) {
if (space->IsImageSpace() || space->IsZygoteSpace()) {
@@ -809,16 +894,23 @@
if (obj == nullptr) {
return true;
}
- return IsAligned<kObjectAlignment>(obj) && IsHeapAddress(obj);
+ return IsAligned<kObjectAlignment>(obj) && FindSpaceFromObject(obj, true) != nullptr;
}
bool Heap::IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const {
return FindContinuousSpaceFromObject(obj, true) != nullptr;
}
-bool Heap::IsHeapAddress(const mirror::Object* obj) const {
- // TODO: This might not work for large objects.
- return FindSpaceFromObject(obj, true) != nullptr;
+bool Heap::IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const {
+ if (obj == nullptr || !IsAligned<kObjectAlignment>(obj)) {
+ return false;
+ }
+ for (const auto& space : continuous_spaces_) {
+ if (space->HasAddress(obj)) {
+ return true;
+ }
+ }
+ return false;
}
bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack,
@@ -1539,6 +1631,7 @@
void Heap::SwapSemiSpaces() {
// Swap the spaces so we allocate into the space which we just evacuated.
std::swap(bump_pointer_space_, temp_space_);
+ bump_pointer_space_->Clear();
}
void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -1616,7 +1709,7 @@
CHECK(temp_space_->IsEmpty());
semi_space_collector_->SetFromSpace(bump_pointer_space_);
semi_space_collector_->SetToSpace(temp_space_);
- mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE);
+ temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
collector = semi_space_collector_;
gc_type = collector::kGcTypeFull;
} else if (current_allocator_ == kAllocatorTypeRosAlloc ||