/*
* 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