Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "verification.h" |
| 18 | |
| 19 | #include <iomanip> |
| 20 | #include <sstream> |
| 21 | |
Andreas Gampe | 5e36c2f | 2017-04-21 19:11:15 -0700 | [diff] [blame] | 22 | #include "art_field-inl.h" |
David Sehr | 891a50e | 2017-10-27 17:01:07 -0700 | [diff] [blame] | 23 | #include "base/file_utils.h" |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 24 | #include "mirror/class-inl.h" |
Mathieu Chartier | 4f5e3cb | 2017-06-12 13:10:01 -0700 | [diff] [blame] | 25 | #include "mirror/object-refvisitor-inl.h" |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 26 | |
| 27 | namespace art { |
| 28 | namespace gc { |
| 29 | |
Mathieu Chartier | b814ef5 | 2017-06-27 12:56:00 -0700 | [diff] [blame] | 30 | std::string Verification::DumpRAMAroundAddress(uintptr_t addr, uintptr_t bytes) const { |
| 31 | const uintptr_t dump_start = addr - bytes; |
| 32 | const uintptr_t dump_end = addr + bytes; |
| 33 | std::ostringstream oss; |
| 34 | if (dump_start < dump_end && |
| 35 | IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_start)) && |
| 36 | IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_end - 1))) { |
| 37 | oss << " adjacent_ram="; |
| 38 | for (uintptr_t p = dump_start; p < dump_end; ++p) { |
| 39 | if (p == addr) { |
| 40 | // Marker of where the address is. |
| 41 | oss << "|"; |
| 42 | } |
| 43 | uint8_t* ptr = reinterpret_cast<uint8_t*>(p); |
| 44 | oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr); |
| 45 | } |
| 46 | } else { |
| 47 | oss << " <invalid address>"; |
| 48 | } |
| 49 | return oss.str(); |
| 50 | } |
| 51 | |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 52 | std::string Verification::DumpObjectInfo(const void* addr, const char* tag) const { |
| 53 | std::ostringstream oss; |
| 54 | oss << tag << "=" << addr; |
| 55 | if (IsValidHeapObjectAddress(addr)) { |
| 56 | mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<void*>(addr)); |
| 57 | mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>(); |
| 58 | oss << " klass=" << klass; |
| 59 | if (IsValidClass(klass)) { |
| 60 | oss << "(" << klass->PrettyClass() << ")"; |
Vladimir Marko | 98db89c | 2018-09-07 11:45:46 +0100 | [diff] [blame^] | 61 | if (klass->IsArrayClass<kVerifyNone>()) { |
| 62 | oss << " length=" << obj->AsArray<kVerifyNone>()->GetLength(); |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 63 | } |
| 64 | } else { |
| 65 | oss << " <invalid address>"; |
| 66 | } |
| 67 | space::Space* const space = heap_->FindSpaceFromAddress(addr); |
| 68 | if (space != nullptr) { |
| 69 | oss << " space=" << *space; |
| 70 | } |
| 71 | accounting::CardTable* card_table = heap_->GetCardTable(); |
| 72 | if (card_table->AddrIsInCardTable(addr)) { |
| 73 | oss << " card=" << static_cast<size_t>( |
| 74 | card_table->GetCard(reinterpret_cast<const mirror::Object*>(addr))); |
| 75 | } |
| 76 | // Dump adjacent RAM. |
Mathieu Chartier | b814ef5 | 2017-06-27 12:56:00 -0700 | [diff] [blame] | 77 | oss << DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(addr), 4 * kObjectAlignment); |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 78 | } else { |
| 79 | oss << " <invalid address>"; |
| 80 | } |
| 81 | return oss.str(); |
| 82 | } |
| 83 | |
| 84 | void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder, |
| 85 | MemberOffset offset, |
| 86 | mirror::Object* ref, |
| 87 | bool fatal) const { |
| 88 | // Lowest priority logging first: |
Andreas Gampe | 170331f | 2017-12-07 18:41:03 -0800 | [diff] [blame] | 89 | PrintFileToLog("/proc/self/maps", android::base::LogSeverity::FATAL_WITHOUT_ABORT); |
Roland Levillain | 680e099 | 2018-08-24 15:41:26 +0100 | [diff] [blame] | 90 | MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), /* terse */ true); |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 91 | // Buffer the output in the string stream since it is more important than the stack traces |
| 92 | // and we want it to have log priority. The stack traces are printed from Runtime::Abort |
| 93 | // which is called from LOG(FATAL) but before the abort message. |
| 94 | std::ostringstream oss; |
| 95 | oss << "GC tried to mark invalid reference " << ref << std::endl; |
| 96 | oss << DumpObjectInfo(ref, "ref") << "\n"; |
Mathieu Chartier | 4ce0c76 | 2017-05-18 10:01:07 -0700 | [diff] [blame] | 97 | oss << DumpObjectInfo(holder.Ptr(), "holder"); |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 98 | if (holder != nullptr) { |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 99 | mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>(); |
| 100 | if (IsValidClass(holder_klass)) { |
Mathieu Chartier | b814ef5 | 2017-06-27 12:56:00 -0700 | [diff] [blame] | 101 | oss << " field_offset=" << offset.Uint32Value(); |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 102 | ArtField* field = holder->FindFieldByOffset(offset); |
| 103 | if (field != nullptr) { |
| 104 | oss << " name=" << field->GetName(); |
| 105 | } |
| 106 | } |
Mathieu Chartier | b814ef5 | 2017-06-27 12:56:00 -0700 | [diff] [blame] | 107 | mirror::HeapReference<mirror::Object>* addr = holder->GetFieldObjectReferenceAddr(offset); |
| 108 | oss << " reference addr" |
| 109 | << DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(addr), 4 * kObjectAlignment); |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | if (fatal) { |
| 113 | LOG(FATAL) << oss.str(); |
| 114 | } else { |
| 115 | LOG(FATAL_WITHOUT_ABORT) << oss.str(); |
| 116 | } |
| 117 | } |
| 118 | |
Mathieu Chartier | b814ef5 | 2017-06-27 12:56:00 -0700 | [diff] [blame] | 119 | bool Verification::IsAddressInHeapSpace(const void* addr, space::Space** out_space) const { |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 120 | space::Space* const space = heap_->FindSpaceFromAddress(addr); |
| 121 | if (space != nullptr) { |
| 122 | if (out_space != nullptr) { |
| 123 | *out_space = space; |
| 124 | } |
| 125 | return true; |
| 126 | } |
| 127 | return false; |
| 128 | } |
| 129 | |
Mathieu Chartier | b814ef5 | 2017-06-27 12:56:00 -0700 | [diff] [blame] | 130 | bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const { |
| 131 | return IsAligned<kObjectAlignment>(addr) && IsAddressInHeapSpace(addr, out_space); |
| 132 | } |
| 133 | |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 134 | bool Verification::IsValidClass(const void* addr) const { |
| 135 | if (!IsValidHeapObjectAddress(addr)) { |
| 136 | return false; |
| 137 | } |
| 138 | mirror::Class* klass = reinterpret_cast<mirror::Class*>(const_cast<void*>(addr)); |
| 139 | mirror::Class* k1 = klass->GetClass<kVerifyNone, kWithoutReadBarrier>(); |
| 140 | if (!IsValidHeapObjectAddress(k1)) { |
| 141 | return false; |
| 142 | } |
Roland Levillain | 001eff9 | 2018-01-24 14:24:33 +0000 | [diff] [blame] | 143 | // `k1` should be class class, take the class again to verify. |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 144 | // Note that this check may not be valid for the no image space since the class class might move |
| 145 | // around from moving GC. |
| 146 | mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>(); |
| 147 | if (!IsValidHeapObjectAddress(k2)) { |
| 148 | return false; |
| 149 | } |
| 150 | return k1 == k2; |
| 151 | } |
| 152 | |
Mathieu Chartier | 4f5e3cb | 2017-06-12 13:10:01 -0700 | [diff] [blame] | 153 | using ObjectSet = std::set<mirror::Object*>; |
| 154 | using WorkQueue = std::deque<std::pair<mirror::Object*, std::string>>; |
| 155 | |
| 156 | // Use for visiting the GcRoots held live by ArtFields, ArtMethods, and ClassLoaders. |
| 157 | class Verification::BFSFindReachable { |
| 158 | public: |
| 159 | explicit BFSFindReachable(ObjectSet* visited) : visited_(visited) {} |
| 160 | |
| 161 | void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const |
| 162 | REQUIRES_SHARED(Locks::mutator_lock_) { |
| 163 | ArtField* field = obj->FindFieldByOffset(offset); |
| 164 | Visit(obj->GetFieldObject<mirror::Object>(offset), |
| 165 | field != nullptr ? field->GetName() : ""); |
| 166 | } |
| 167 | |
| 168 | void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const |
| 169 | REQUIRES_SHARED(Locks::mutator_lock_) { |
| 170 | if (!root->IsNull()) { |
| 171 | VisitRoot(root); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const |
| 176 | REQUIRES_SHARED(Locks::mutator_lock_) { |
| 177 | Visit(root->AsMirrorPtr(), "!nativeRoot"); |
| 178 | } |
| 179 | |
| 180 | void Visit(mirror::Object* ref, const std::string& field_name) const |
| 181 | REQUIRES_SHARED(Locks::mutator_lock_) { |
| 182 | if (ref != nullptr && visited_->insert(ref).second) { |
| 183 | new_visited_.emplace_back(ref, field_name); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | const WorkQueue& NewlyVisited() const { |
| 188 | return new_visited_; |
| 189 | } |
| 190 | |
| 191 | private: |
| 192 | ObjectSet* visited_; |
| 193 | mutable WorkQueue new_visited_; |
| 194 | }; |
| 195 | |
| 196 | class Verification::CollectRootVisitor : public SingleRootVisitor { |
| 197 | public: |
| 198 | CollectRootVisitor(ObjectSet* visited, WorkQueue* work) : visited_(visited), work_(work) {} |
| 199 | |
| 200 | void VisitRoot(mirror::Object* obj, const RootInfo& info) |
Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 201 | override REQUIRES_SHARED(Locks::mutator_lock_) { |
Mathieu Chartier | 4f5e3cb | 2017-06-12 13:10:01 -0700 | [diff] [blame] | 202 | if (obj != nullptr && visited_->insert(obj).second) { |
| 203 | std::ostringstream oss; |
| 204 | oss << info.ToString() << " = " << obj << "(" << obj->PrettyTypeOf() << ")"; |
| 205 | work_->emplace_back(obj, oss.str()); |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | private: |
| 210 | ObjectSet* const visited_; |
| 211 | WorkQueue* const work_; |
| 212 | }; |
| 213 | |
| 214 | std::string Verification::FirstPathFromRootSet(ObjPtr<mirror::Object> target) const { |
| 215 | Runtime* const runtime = Runtime::Current(); |
| 216 | std::set<mirror::Object*> visited; |
| 217 | std::deque<std::pair<mirror::Object*, std::string>> work; |
| 218 | { |
| 219 | CollectRootVisitor root_visitor(&visited, &work); |
| 220 | runtime->VisitRoots(&root_visitor, kVisitRootFlagAllRoots); |
| 221 | } |
| 222 | while (!work.empty()) { |
| 223 | auto pair = work.front(); |
| 224 | work.pop_front(); |
| 225 | if (pair.first == target) { |
| 226 | return pair.second; |
| 227 | } |
| 228 | BFSFindReachable visitor(&visited); |
| 229 | pair.first->VisitReferences(visitor, VoidFunctor()); |
| 230 | for (auto&& pair2 : visitor.NewlyVisited()) { |
| 231 | std::ostringstream oss; |
| 232 | mirror::Object* obj = pair2.first; |
| 233 | oss << pair.second << " -> " << obj << "(" << obj->PrettyTypeOf() << ")." << pair2.second; |
| 234 | work.emplace_back(obj, oss.str()); |
| 235 | } |
| 236 | } |
| 237 | return "<no path found>"; |
| 238 | } |
| 239 | |
Mathieu Chartier | 1ca6890 | 2017-04-18 11:26:22 -0700 | [diff] [blame] | 240 | } // namespace gc |
| 241 | } // namespace art |