diff options
Diffstat (limited to 'runtime/gc/verification.cc')
| -rw-r--r-- | runtime/gc/verification.cc | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc new file mode 100644 index 0000000000..c14f250528 --- /dev/null +++ b/runtime/gc/verification.cc @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "verification.h" + +#include <iomanip> +#include <sstream> + +#include "art_field-inl.h" +#include "mirror/class-inl.h" + +namespace art { +namespace gc { + +std::string Verification::DumpObjectInfo(const void* addr, const char* tag) const { + std::ostringstream oss; + oss << tag << "=" << addr; + if (IsValidHeapObjectAddress(addr)) { + mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<void*>(addr)); + mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>(); + oss << " klass=" << klass; + if (IsValidClass(klass)) { + oss << "(" << klass->PrettyClass() << ")"; + if (klass->IsArrayClass<kVerifyNone, kWithoutReadBarrier>()) { + oss << " length=" << obj->AsArray<kVerifyNone, kWithoutReadBarrier>()->GetLength(); + } + } else { + oss << " <invalid address>"; + } + space::Space* const space = heap_->FindSpaceFromAddress(addr); + if (space != nullptr) { + oss << " space=" << *space; + } + accounting::CardTable* card_table = heap_->GetCardTable(); + if (card_table->AddrIsInCardTable(addr)) { + oss << " card=" << static_cast<size_t>( + card_table->GetCard(reinterpret_cast<const mirror::Object*>(addr))); + } + // Dump adjacent RAM. + const uintptr_t uint_addr = reinterpret_cast<uintptr_t>(addr); + static constexpr size_t kBytesBeforeAfter = 2 * kObjectAlignment; + const uintptr_t dump_start = uint_addr - kBytesBeforeAfter; + const uintptr_t dump_end = uint_addr + kBytesBeforeAfter; + if (dump_start < dump_end && + IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_start)) && + IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_end - kObjectAlignment))) { + oss << " adjacent_ram="; + for (uintptr_t p = dump_start; p < dump_end; ++p) { + if (p == uint_addr) { + // Marker of where the object is. + oss << "|"; + } + uint8_t* ptr = reinterpret_cast<uint8_t*>(p); + oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr); + } + } + } else { + oss << " <invalid address>"; + } + return oss.str(); +} + +void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder, + MemberOffset offset, + mirror::Object* ref, + bool fatal) const { + // Lowest priority logging first: + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); + // Buffer the output in the string stream since it is more important than the stack traces + // and we want it to have log priority. The stack traces are printed from Runtime::Abort + // which is called from LOG(FATAL) but before the abort message. + std::ostringstream oss; + oss << "GC tried to mark invalid reference " << ref << std::endl; + oss << DumpObjectInfo(ref, "ref") << "\n"; + oss << DumpObjectInfo(holder.Ptr(), "holder"); + if (holder != nullptr) { + mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>(); + if (IsValidClass(holder_klass)) { + oss << "field_offset=" << offset.Uint32Value(); + ArtField* field = holder->FindFieldByOffset(offset); + if (field != nullptr) { + oss << " name=" << field->GetName(); + } + } + } + + if (fatal) { + LOG(FATAL) << oss.str(); + } else { + LOG(FATAL_WITHOUT_ABORT) << oss.str(); + } +} + +bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const { + if (!IsAligned<kObjectAlignment>(addr)) { + return false; + } + space::Space* const space = heap_->FindSpaceFromAddress(addr); + if (space != nullptr) { + if (out_space != nullptr) { + *out_space = space; + } + return true; + } + return false; +} + +bool Verification::IsValidClass(const void* addr) const { + if (!IsValidHeapObjectAddress(addr)) { + return false; + } + mirror::Class* klass = reinterpret_cast<mirror::Class*>(const_cast<void*>(addr)); + mirror::Class* k1 = klass->GetClass<kVerifyNone, kWithoutReadBarrier>(); + if (!IsValidHeapObjectAddress(k1)) { + return false; + } + // k should be class class, take the class again to verify. + // Note that this check may not be valid for the no image space since the class class might move + // around from moving GC. + mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>(); + if (!IsValidHeapObjectAddress(k2)) { + return false; + } + return k1 == k2; +} + +} // namespace gc +} // namespace art |