Changeset 213883 in webkit
- Timestamp:
- Mar 13, 2017 5:39:24 PM (7 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r213881 r213883 1 2017-03-13 Mark Lam <mark.lam@apple.com> 2 3 Make the HeapVerifier useful again. 4 https://bugs.webkit.org/show_bug.cgi?id=161752 5 6 Reviewed by Filip Pizlo. 7 8 Resurrect the HeapVerifier. Here's what the verifier now offers: 9 10 1. It captures the list of cells before and after GCs up to N GC cycles. 11 N is set by JSC_numberOfGCCyclesToRecordForVerification. 12 Currently, N defaults to 3. 13 14 This is useful if we're debugging in lldb and want to check if a candidate 15 cell pointer was observed by the GC during the last N GC cycles. We can do 16 this check buy calling HeapVerifier::checkIfRecorded() with the cell address. 17 18 HeapVerifier::checkIfRecorded() is robust and can be used on bogus addresses. 19 If the candidate cell was previously recorded by the HeapVerifier during a 20 GC cycle, checkIfRecorded() will dump any useful info it has on that cell. 21 22 2. The HeapVerifier will verify that cells in its captured list after a GC are 23 sane. Some examples of cell insanity are: 24 - the cell claims to belong to a different VM. 25 - the cell has a NULL structureID. 26 - the cell has a NULL structure. 27 - the cell's structure has a NULL structureID. 28 - the cell's structure has a NULL structure. 29 - the cell's structure's structure has a NULL structureID. 30 - the cell's structure's structure has a NULL structure. 31 32 These are all signs of corruption or a GC bug. The verifier will report any 33 insanity it finds, and then crash with a RELEASE_ASSERT. 34 35 3. Since the HeapVerifier captures list of cells in the heap before and after GCs 36 for the last N GCs, it will also automatically "trim" dead cells those list 37 after the most recent GC. 38 39 "trim" here means that the CellProfile in the HeapVerifier's lists will be 40 updated to reflect that the cell is now dead. It still keeps a record of the 41 dead cell pointer and the meta data collected about it back when it was alive. 42 As a result, checkIfRecorded() will also report if the candidate cell passed 43 to it is a dead object from a previous GC cycle. 44 45 4. Each CellProfile captured by the HeapVerifier now track the following info: 46 - the cell's HeapCell::Kind. 47 - the cell's liveness. 48 - if is JSCell, the cell's classInfo()->className. 49 - an associated timestamp. 50 - an associated stack trace. 51 52 Currently, the timestamp is only used for the time when the cell was recorded 53 by the HeapVerifier during GC. The stack trace is currently unused. 54 55 However, these fields are kept there so that we can instrument the VM (during 56 a debugging session, which requires rebuilding the VM) and record interesting 57 stack traces like that of the time of allocation of the cell. Since 58 capturing the stack traces for each cell is a very heavy weight operation, 59 the HeapVerifier code does not do this by default. Instead, we just leave 60 the building blocks for doing so in place to ease future debugging efforts. 61 62 * heap/Heap.cpp: 63 (JSC::Heap::runBeginPhase): 64 (JSC::Heap::runEndPhase): 65 (JSC::Heap::didFinishCollection): 66 * heap/Heap.h: 67 (JSC::Heap::verifier): 68 * heap/MarkedAllocator.h: 69 (JSC::MarkedAllocator::takeLastActiveBlock): Deleted. 70 * heap/MarkedSpace.h: 71 * heap/MarkedSpaceInlines.h: 72 (JSC::MarkedSpace::forEachLiveCell): 73 * tools/CellList.cpp: 74 (JSC::CellList::find): 75 (JSC::CellList::reset): 76 (JSC::CellList::findCell): Deleted. 77 * tools/CellList.h: 78 (JSC::CellList::CellList): 79 (JSC::CellList::name): 80 (JSC::CellList::size): 81 (JSC::CellList::cells): 82 (JSC::CellList::add): 83 (JSC::CellList::reset): Deleted. 84 * tools/CellProfile.h: 85 (JSC::CellProfile::CellProfile): 86 (JSC::CellProfile::cell): 87 (JSC::CellProfile::jsCell): 88 (JSC::CellProfile::isJSCell): 89 (JSC::CellProfile::kind): 90 (JSC::CellProfile::isLive): 91 (JSC::CellProfile::isDead): 92 (JSC::CellProfile::setIsLive): 93 (JSC::CellProfile::setIsDead): 94 (JSC::CellProfile::timestamp): 95 (JSC::CellProfile::className): 96 (JSC::CellProfile::stackTrace): 97 (JSC::CellProfile::setStackTrace): 98 * tools/HeapVerifier.cpp: 99 (JSC::HeapVerifier::startGC): 100 (JSC::HeapVerifier::endGC): 101 (JSC::HeapVerifier::gatherLiveCells): 102 (JSC::trimDeadCellsFromList): 103 (JSC::HeapVerifier::trimDeadCells): 104 (JSC::HeapVerifier::printVerificationHeader): 105 (JSC::HeapVerifier::verifyCellList): 106 (JSC::HeapVerifier::validateCell): 107 (JSC::HeapVerifier::validateJSCell): 108 (JSC::HeapVerifier::verify): 109 (JSC::HeapVerifier::reportCell): 110 (JSC::HeapVerifier::checkIfRecorded): 111 (JSC::HeapVerifier::initializeGCCycle): Deleted. 112 (JSC::GatherCellFunctor::GatherCellFunctor): Deleted. 113 (JSC::GatherCellFunctor::visit): Deleted. 114 (JSC::GatherCellFunctor::operator()): Deleted. 115 (JSC::HeapVerifier::verifyButterflyIsInStorageSpace): Deleted. 116 * tools/HeapVerifier.h: 117 (JSC::HeapVerifier::GCCycle::reset): 118 1 119 2017-03-13 SKumarMetro <s.kumar@metrological.com> 2 120 -
trunk/Source/JavaScriptCore/heap/Heap.cpp
r213675 r213883 1098 1098 willStartCollection(scope); 1099 1099 1100 if ( m_verifier) {1100 if (UNLIKELY(m_verifier)) { 1101 1101 // Verify that live objects from the last GC cycle haven't been corrupted by 1102 1102 // mutators before we begin this new GC cycle. 1103 1103 m_verifier->verify(HeapVerifier::Phase::BeforeGC); 1104 1104 1105 m_verifier-> initializeGCCycle();1105 m_verifier->startGC(); 1106 1106 m_verifier->gatherLiveCells(HeapVerifier::Phase::BeforeMarking); 1107 1107 } … … 1333 1333 endMarking(); 1334 1334 1335 if ( m_verifier) {1335 if (UNLIKELY(m_verifier)) { 1336 1336 m_verifier->gatherLiveCells(HeapVerifier::Phase::AfterMarking); 1337 1337 m_verifier->verify(HeapVerifier::Phase::AfterMarking); … … 1358 1358 updateAllocationLimits(); 1359 1359 1360 didFinishCollection(); 1361 1362 if (m_verifier) { 1360 if (UNLIKELY(m_verifier)) { 1363 1361 m_verifier->trimDeadCells(); 1364 1362 m_verifier->verify(HeapVerifier::Phase::AfterGC); 1365 1363 } 1364 1365 didFinishCollection(); 1366 1366 1367 1367 if (false) { … … 2163 2163 } 2164 2164 2165 if (UNLIKELY(m_verifier)) 2166 m_verifier->endGC(); 2167 2165 2168 RELEASE_ASSERT(m_collectionScope); 2166 2169 m_lastCollectionScope = m_collectionScope; -
trunk/Source/JavaScriptCore/heap/Heap.h
r213652 r213883 352 352 JS_EXPORT_PRIVATE void setRunLoop(CFRunLoopRef); 353 353 #endif // USE(CF) 354 354 355 HeapVerifier* verifier() const { return m_verifier.get(); } 356 355 357 private: 356 358 friend class AllocatingScope; -
trunk/Source/JavaScriptCore/heap/MarkedAllocator.h
r210844 r213883 43 43 macro(live, Live) /* The set of block indices that have actual blocks. */\ 44 44 macro(empty, Empty) /* The set of all blocks that have no live objects and nothing to destroy. */ \ 45 macro(allocated, Allocated) /* The set of all blocks that are full of live objects. */\45 macro(allocated, Allocated) /* The set of all blocks that are full of live objects. */\ 46 46 macro(canAllocateButNotEmpty, CanAllocateButNotEmpty) /* The set of all blocks are neither empty nor retired (i.e. are more than minMarkedBlockUtilization full). */ \ 47 47 macro(eden, Eden) /* The set of all blocks that have new objects since the last GC. */\ … … 156 156 void* tryAllocate(GCDeferralContext* = nullptr); 157 157 Heap* heap() { return m_heap; } 158 MarkedBlock::Handle* takeLastActiveBlock() 159 { 160 MarkedBlock::Handle* block = m_lastActiveBlock; 161 m_lastActiveBlock = 0; 162 return block; 163 } 164 158 165 159 template<typename Functor> void forEachBlock(const Functor&); 166 160 template<typename Functor> void forEachNotEmptyBlock(const Functor&); -
trunk/Source/JavaScriptCore/heap/MarkedSpace.h
r210844 r213883 179 179 void* tryAllocateSlow(Subspace&, GCDeferralContext*, size_t); 180 180 181 // Use this version when calling from within the GC where we know that the allocators 182 // have already been stopped. 183 template<typename Functor> void forEachLiveCell(const Functor&); 184 181 185 static void initializeSizeClassForStepSize(); 182 186 … … 213 217 MarkedAllocator* m_lastAllocator { nullptr }; 214 218 MarkedAllocator* m_allocatorForEmptyAllocation { nullptr }; 219 220 friend class HeapVerifier; 215 221 }; 216 222 -
trunk/Source/JavaScriptCore/heap/MarkedSpaceInlines.h
r206154 r213883 1 1 /* 2 * Copyright (C) 2016 Apple Inc. All rights reserved.2 * Copyright (C) 2016-2017 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 34 34 { 35 35 ASSERT(isIterating()); 36 forEachLiveCell(functor); 37 } 38 39 template<typename Functor> inline void MarkedSpace::forEachLiveCell(const Functor& functor) 40 { 36 41 BlockIterator end = m_blocks.set().end(); 37 42 for (BlockIterator it = m_blocks.set().begin(); it != end; ++it) { -
trunk/Source/JavaScriptCore/tools/CellList.cpp
r213675 r213883 29 29 namespace JSC { 30 30 31 CellProfile* CellList::find Cell(JSCell* cell)31 CellProfile* CellList::find(HeapCell* cell) 32 32 { 33 for (auto& profile : liveCells) { 34 if (cell == profile.cell) 35 return &profile; 33 if (!size()) 34 return nullptr; 35 36 if (!m_mapIsUpToDate) { 37 m_map.clear(); 38 for (auto& profile : m_cells) 39 m_map.add(profile.cell(), &profile); 40 m_mapIsUpToDate = true; 36 41 } 37 return nullptr; 42 return m_map.get(cell); 43 } 44 45 void CellList::reset() 46 { 47 m_cells.clear(); 48 m_map.clear(); 49 m_mapIsUpToDate = false; 38 50 } 39 51 -
trunk/Source/JavaScriptCore/tools/CellList.h
r213675 r213883 27 27 28 28 #include "CellProfile.h" 29 #include <wtf/Vector.h> 29 #include <wtf/HashMap.h> 30 #include <wtf/SegmentedVector.h> 30 31 31 32 namespace JSC { 32 33 33 struct CellList { 34 class CellList { 35 WTF_MAKE_FAST_ALLOCATED; 36 public: 34 37 CellList(const char* name) 35 : name(name) 36 , hasLiveCells(true) 38 : m_name(name) 37 39 { 38 40 } 39 41 40 void reset() 42 const char* name() const { return m_name; } 43 size_t size() const { return m_cells.size(); } 44 45 typedef SegmentedVector<CellProfile, 64> CellProfileVector; 46 CellProfileVector& cells() { return m_cells; } 47 48 void add(CellProfile&& profile) 41 49 { 42 liveCells.clear();43 hasLiveCells = true; // Presume to have live objects until the list is trimmed.50 m_cells.append(WTFMove(profile)); 51 m_mapIsUpToDate = false; 44 52 } 45 46 CellProfile* findCell(JSCell*); 47 48 const char* name; 49 Vector<CellProfile> liveCells; 50 bool hasLiveCells; 53 54 void reset(); 55 56 CellProfile* find(HeapCell*); 57 58 private: 59 const char* m_name; 60 CellProfileVector m_cells; 61 62 bool m_mapIsUpToDate { false }; 63 HashMap<HeapCell*, CellProfile*> m_map; 51 64 }; 52 65 -
trunk/Source/JavaScriptCore/tools/CellProfile.h
r213675 r213883 26 26 #pragma once 27 27 28 #include "JSCell.h" 29 #include "StackTrace.h" 30 #include "Structure.h" 31 #include <wtf/MonotonicTime.h> 32 28 33 namespace JSC { 29 34 30 class JSCell; 35 struct CellProfile { 36 enum Liveness { 37 Unknown, 38 Dead, 39 Live 40 }; 31 41 32 struct CellProfile { 33 CellProfile(JSCell* cell, bool isConfirmedDead = false) 34 : cell(cell) 35 , isConfirmedDead(isConfirmedDead) 42 CellProfile(HeapCell* cell, HeapCell::Kind kind, Liveness liveness) 43 : m_cell(cell) 44 , m_kind(kind) 45 , m_liveness(liveness) 46 , m_timestamp(MonotonicTime::now()) 36 47 { 48 if (m_kind == HeapCell::JSCell && m_liveness != Dead) 49 m_className = jsCell()->structure()->classInfo()->className; 37 50 } 51 52 CellProfile(CellProfile&& other) 53 : m_cell(other.m_cell) 54 , m_kind(other.m_kind) 55 , m_liveness(other.m_liveness) 56 , m_timestamp(other.m_timestamp) 57 , m_className(other.m_className) 58 , m_stackTrace(WTFMove(other.m_stackTrace)) 59 { } 60 61 HeapCell* cell() const { return m_cell; } 62 JSCell* jsCell() const 63 { 64 ASSERT(isJSCell()); 65 return static_cast<JSCell*>(m_cell); 66 } 67 68 bool isJSCell() const { return m_kind == HeapCell::JSCell; } 38 69 39 JSCell* cell; 40 bool isConfirmedDead; 70 HeapCell::Kind kind() const { return m_kind; } 71 72 bool isLive() const { return m_liveness == Live; } 73 bool isDead() const { return m_liveness == Dead; } 74 75 void setIsLive() { m_liveness = Live; } 76 void setIsDead() { m_liveness = Dead; } 77 78 MonotonicTime timestamp() const { return m_timestamp; } 79 80 const char* className() const { return m_className; } 81 82 StackTrace* stackTrace() const { return m_stackTrace.get(); } 83 void setStackTrace(StackTrace* trace) { m_stackTrace = std::unique_ptr<StackTrace>(trace); } 84 85 private: 86 HeapCell* m_cell; 87 HeapCell::Kind m_kind; 88 Liveness m_liveness { Unknown }; 89 MonotonicTime m_timestamp; 90 const char* m_className { nullptr }; 91 std::unique_ptr<StackTrace> m_stackTrace; 41 92 }; 42 93 -
trunk/Source/JavaScriptCore/tools/HeapVerifier.cpp
r213675 r213883 1 1 /* 2 * Copyright (C) 2014 , 2016Apple Inc. All rights reserved.2 * Copyright (C) 2014-2017 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 27 27 #include "HeapVerifier.h" 28 28 29 #include " ButterflyInlines.h"29 #include "CodeBlock.h" 30 30 #include "HeapIterationScope.h" 31 31 #include "JSCInlines.h" 32 32 #include "JSObject.h" 33 33 #include "MarkedSpaceInlines.h" 34 #include "VMInspector.h" 35 #include "ValueProfile.h" 36 #include <wtf/ProcessID.h> 34 37 35 38 namespace JSC { … … 60 63 } 61 64 62 void HeapVerifier:: initializeGCCycle()65 void HeapVerifier::startGC() 63 66 { 64 67 Heap* heap = m_heap; 65 68 incrementCycle(); 69 currentCycle().reset(); 66 70 currentCycle().scope = *heap->collectionScope(); 67 } 68 69 struct GatherCellFunctor : MarkedBlock::CountFunctor { 70 GatherCellFunctor(CellList& list) 71 : m_list(list) 72 { 73 ASSERT(!list.liveCells.size()); 74 } 75 76 inline void visit(JSCell* cell) 77 { 78 CellProfile profile(cell); 79 m_list.liveCells.append(profile); 80 } 81 82 IterationStatus operator()(HeapCell* cell, HeapCell::Kind kind) const 83 { 84 if (kind == HeapCell::JSCell) { 85 // FIXME: This const_cast exists because this isn't a C++ lambda. 86 // https://bugs.webkit.org/show_bug.cgi?id=159644 87 const_cast<GatherCellFunctor*>(this)->visit(static_cast<JSCell*>(cell)); 88 } 89 return IterationStatus::Continue; 90 } 91 92 CellList& m_list; 93 }; 71 currentCycle().timestamp = MonotonicTime::now(); 72 ASSERT(!m_didPrintLogs); 73 } 74 75 void HeapVerifier::endGC() 76 { 77 if (m_didPrintLogs) { 78 dataLog("END "); 79 printVerificationHeader(); 80 dataLog("\n\n"); 81 m_didPrintLogs = false; 82 } 83 } 94 84 95 85 void HeapVerifier::gatherLiveCells(HeapVerifier::Phase phase) … … 98 88 CellList& list = *cellListForGathering(phase); 99 89 100 HeapIterationScope iterationScope(*heap);101 90 list.reset(); 102 GatherCellFunctor functor(list); 103 heap->m_objectSpace.forEachLiveCell(iterationScope, functor); 91 heap->m_objectSpace.forEachLiveCell([&list] (HeapCell* cell, HeapCell::Kind kind) { 92 list.add({ cell, kind, CellProfile::Live }); 93 return IterationStatus::Continue; 94 }); 104 95 } 105 96 … … 120 111 } 121 112 122 static void trimDeadCellsFromList( HashSet<JSCell*>& knownLiveSet, CellList& list)123 { 124 if (!list. hasLiveCells)113 static void trimDeadCellsFromList(CellList& knownLiveSet, CellList& list) 114 { 115 if (!list.size()) 125 116 return; 126 117 127 size_t liveCellsFound = 0; 128 for (auto& cellProfile : list.liveCells) { 129 if (cellProfile.isConfirmedDead) 118 for (auto& cellProfile : list.cells()) { 119 if (cellProfile.isDead()) 130 120 continue; // Don't "resurrect" known dead cells. 131 if (!knownLiveSet. contains(cellProfile.cell)) {132 cellProfile. isConfirmedDead = true;121 if (!knownLiveSet.find(cellProfile.cell())) { 122 cellProfile.setIsDead(); 133 123 continue; 134 124 } 135 liveCellsFound++; 136 } 137 list.hasLiveCells = !!liveCellsFound; 125 cellProfile.setIsLive(); 126 } 138 127 } 139 128 140 129 void HeapVerifier::trimDeadCells() 141 130 { 142 HashSet<JSCell*> knownLiveSet; 143 144 CellList& after = currentCycle().after; 145 for (auto& cellProfile : after.liveCells) 146 knownLiveSet.add(cellProfile.cell); 131 CellList& knownLiveSet = currentCycle().after; 147 132 148 133 trimDeadCellsFromList(knownLiveSet, currentCycle().before); … … 154 139 } 155 140 156 bool HeapVerifier::verifyButterflyIsInStorageSpace(Phase, CellList&) 157 { 158 // FIXME: Make this work again. https://bugs.webkit.org/show_bug.cgi?id=161752 141 void HeapVerifier::printVerificationHeader() 142 { 143 RELEASE_ASSERT(m_heap->collectionScope()); 144 CollectionScope scope = currentCycle().scope; 145 MonotonicTime gcCycleTimestamp = currentCycle().timestamp; 146 dataLog("Verifying heap in [p", getCurrentProcessID(), ", t", currentThread(), "] vm ", 147 RawPointer(m_heap->vm()), " on ", scope, " GC @ ", gcCycleTimestamp, "\n"); 148 } 149 150 bool HeapVerifier::verifyCellList(Phase phase, CellList& list) 151 { 152 VM& vm = *m_heap->vm(); 153 auto& liveCells = list.cells(); 154 155 bool listNamePrinted = false; 156 auto printHeaderIfNeeded = [&] () { 157 if (listNamePrinted) 158 return; 159 160 printVerificationHeader(); 161 dataLog(" @ phase ", phaseName(phase), ": FAILED in cell list '", list.name(), "' (size ", liveCells.size(), ")\n"); 162 listNamePrinted = true; 163 m_didPrintLogs = true; 164 }; 165 166 bool success = true; 167 for (size_t i = 0; i < liveCells.size(); i++) { 168 CellProfile& profile = liveCells[i]; 169 if (!profile.isLive()) 170 continue; 171 172 if (!profile.isJSCell()) 173 continue; 174 175 JSCell* cell = profile.jsCell(); 176 success |= validateJSCell(&vm, cell, &profile, &list, printHeaderIfNeeded, " "); 177 } 178 179 return success; 180 } 181 182 bool HeapVerifier::validateCell(HeapCell* cell, VM* expectedVM) 183 { 184 auto printNothing = [] () { }; 185 186 if (cell->isZapped()) { 187 dataLog(" cell ", RawPointer(cell), " is ZAPPED\n"); 188 return false; 189 } 190 191 if (cell->cellKind() != HeapCell::JSCell) 192 return true; // Nothing more to validate. 193 194 JSCell* jsCell = static_cast<JSCell*>(cell); 195 return validateJSCell(expectedVM, jsCell, nullptr, nullptr, printNothing); 196 } 197 198 bool HeapVerifier::validateJSCell(VM* expectedVM, JSCell* cell, CellProfile* profile, CellList* list, std::function<void()> printHeaderIfNeeded, const char* prefix) 199 { 200 auto printHeaderAndCell = [cell, profile, printHeaderIfNeeded, prefix] () { 201 printHeaderIfNeeded(); 202 dataLog(prefix, "cell ", RawPointer(cell)); 203 if (profile) 204 dataLog(" [", profile->className(), "]"); 205 }; 206 207 // 1. Validate the cell. 208 209 if (cell->isZapped()) { 210 printHeaderAndCell(); 211 dataLog(" is zapped\n"); 212 return false; 213 } 214 215 StructureID structureID = cell->structureID(); 216 if (!structureID) { 217 printHeaderAndCell(); 218 dataLog(" has NULL structureID\n"); 219 return false; 220 } 221 222 if (expectedVM) { 223 VM& vm = *expectedVM; 224 225 VM* cellVM = cell->vm(); 226 if (cellVM != expectedVM) { 227 printHeaderAndCell(); 228 dataLog(" is from a different VM: expected:", RawPointer(expectedVM), " actual:", RawPointer(cellVM), "\n"); 229 return false; 230 } 231 232 // 2. Validate the cell's structure 233 234 Structure* structure = vm.getStructure(structureID); 235 if (!structure) { 236 printHeaderAndCell(); 237 #if USE(JSVALUE64) 238 uint32_t structureIDAsUint32 = structureID; 239 #else 240 uint32_t structureIDAsUint32 = reinterpret_cast<uint32_t>(structureID); 241 #endif 242 dataLog(" with structureID ", structureIDAsUint32, " maps to a NULL Structure pointer\n"); 243 return false; 244 } 245 246 if (structure->isZapped()) { 247 printHeaderAndCell(); 248 dataLog(" has ZAPPED structure ", RawPointer(structure), "\n"); 249 return false; 250 } 251 252 if (!structure->structureID()) { 253 printHeaderAndCell(); 254 dataLog(" has structure ", RawPointer(structure), " whose structureID is NULL\n"); 255 return false; 256 } 257 258 VM* structureVM = structure->vm(); 259 if (structureVM != expectedVM) { 260 printHeaderAndCell(); 261 dataLog(" has structure ", RawPointer(structure), " from a different VM: expected:", RawPointer(expectedVM), " actual:", RawPointer(structureVM), "\n"); 262 return false; 263 } 264 265 if (list) { 266 auto* structureProfile = list->find(structure); 267 if (!structureProfile) { 268 printHeaderAndCell(); 269 dataLog(" has structure ", RawPointer(structure), " NOT found in the live cell list\n"); 270 return false; 271 } 272 273 if (!structureProfile->isLive()) { 274 printHeaderAndCell(); 275 dataLog(" has DEAD structure ", RawPointer(structure), "\n"); 276 return false; 277 } 278 } 279 280 StructureID structureStructureID = structure->structureID(); 281 if (!structureStructureID) { 282 printHeaderAndCell(); 283 dataLog(" has structure ", RawPointer(structure), " with a NULL structureID\n"); 284 return false; 285 } 286 287 // 3. Validate the cell's structure's structure. 288 289 Structure* structureStructure = vm.getStructure(structureID); 290 if (!structureStructure) { 291 printHeaderAndCell(); 292 dataLog(" has structure ", RawPointer(structure), " whose structure is NULL\n"); 293 return false; 294 } 295 296 if (structureStructure->isZapped()) { 297 printHeaderAndCell(); 298 dataLog(" has structure ", RawPointer(structure), " whose structure ", RawPointer(structureStructure), " is ZAPPED\n"); 299 return false; 300 } 301 302 if (!structureStructure->structureID()) { 303 printHeaderAndCell(); 304 dataLog(" has structure ", RawPointer(structure), " whose structure ", RawPointer(structureStructure), " has a NULL structureID\n"); 305 return false; 306 } 307 308 VM* structureStructureVM = structureStructure->vm(); 309 if (structureStructureVM != expectedVM) { 310 printHeaderAndCell(); 311 dataLog(" has structure ", RawPointer(structure), " whose structure ", RawPointer(structureStructure), " is from a different VM: expected:", RawPointer(expectedVM), " actual:", RawPointer(structureStructureVM), "\n"); 312 return false; 313 } 314 315 if (list) { 316 auto* structureStructureProfile = list->find(structureStructure); 317 if (!structureStructureProfile) { 318 printHeaderAndCell(); 319 dataLog(" has structure ", RawPointer(structure), " whose structure ", RawPointer(structureStructure), " is NOT found in the live cell list\n"); 320 return false; 321 } 322 323 if (!structureStructureProfile->isLive()) { 324 printHeaderAndCell(); 325 dataLog(" has structure ", RawPointer(structure), " whose structure ", RawPointer(structureStructure), " is DEAD\n"); 326 return false; 327 } 328 } 329 330 CodeBlock* codeBlock = jsDynamicCast<CodeBlock*>(vm, cell); 331 if (UNLIKELY(codeBlock)) { 332 bool success = true; 333 for (unsigned i = 0; i < codeBlock->totalNumberOfValueProfiles(); ++i) { 334 ValueProfile* valueProfile = codeBlock->getFromAllValueProfiles(i); 335 for (unsigned i = 0; i < ValueProfile::totalNumberOfBuckets; ++i) { 336 JSValue value = JSValue::decode(valueProfile->m_buckets[i]); 337 if (!value) 338 continue; 339 if (!value.isCell()) 340 continue; 341 JSCell* valueCell = value.asCell(); 342 if (valueCell->isZapped()) { 343 printHeaderIfNeeded(); 344 dataLog(prefix, "CodeBlock ", RawPointer(codeBlock), " has ZAPPED ValueProfile cell ", RawPointer(valueCell), "\n"); 345 success = false; 346 continue; 347 } 348 } 349 } 350 if (!success) 351 return false; 352 } 353 } 354 159 355 return true; 160 356 } … … 162 358 void HeapVerifier::verify(HeapVerifier::Phase phase) 163 359 { 164 bool beforeVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().before); 165 bool afterVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().after); 166 RELEASE_ASSERT(beforeVerified && afterVerified); 167 } 168 169 void HeapVerifier::reportCell(CellProfile& cellProfile, int cycleIndex, HeapVerifier::GCCycle& cycle, CellList& list) 170 { 171 JSCell* cell = cellProfile.cell; 172 173 if (cellProfile.isConfirmedDead) { 174 dataLogF("FOUND dead cell %p in GC[%d] %s list '%s'\n", 175 cell, cycleIndex, collectionScopeName(cycle.scope), list.name); 176 return; 177 } 178 179 if (cell->isObject()) { 180 JSObject* object = static_cast<JSObject*>(cell); 181 Structure* structure = object->structure(); 182 Butterfly* butterfly = object->butterfly(); 183 void* butterflyBase = butterfly->base(structure); 184 185 dataLogF("FOUND object %p type '%s' butterfly %p (base %p) in GC[%d] %s list '%s'\n", 186 object, structure->classInfo()->className, 187 butterfly, butterflyBase, 188 cycleIndex, collectionScopeName(cycle.scope), list.name); 189 } else { 190 Structure* structure = cell->structure(); 191 dataLogF("FOUND cell %p type '%s' in GC[%d] %s list '%s'\n", 192 cell, structure->classInfo()->className, 193 cycleIndex, collectionScopeName(cycle.scope), list.name); 194 } 195 } 196 197 void HeapVerifier::checkIfRecorded(JSCell* cell) 360 if (phase == Phase::AfterGC) { 361 bool verified = verifyCellList(phase, currentCycle().after); 362 RELEASE_ASSERT(verified); 363 } 364 } 365 366 void HeapVerifier::reportCell(CellProfile& profile, int cycleIndex, HeapVerifier::GCCycle& cycle, CellList& list, const char* prefix) 367 { 368 HeapCell* cell = profile.cell(); 369 VM* vm = m_heap->vm(); 370 371 if (prefix) 372 dataLog(prefix); 373 374 dataLog("FOUND"); 375 if (profile.isLive()) 376 dataLog(" LIVE"); 377 else if (profile.isDead()) 378 dataLog(" DEAD"); 379 380 if (!profile.isJSCell()) 381 dataLog(" HeapCell "); 382 else 383 dataLog(" JSCell "); 384 dataLog(RawPointer(cell)); 385 386 if (profile.className()) 387 dataLog(" [", profile.className(), "]"); 388 389 if (profile.isLive() && profile.isJSCell()) { 390 JSCell* jsCell = profile.jsCell(); 391 Structure* structure = jsCell->structure(); 392 dataLog(" structure:", RawPointer(structure)); 393 if (jsCell->isObject()) { 394 JSObject* obj = static_cast<JSObject*>(cell); 395 Butterfly* butterfly = obj->butterfly(); 396 void* butterflyBase = butterfly->base(structure); 397 398 dataLog(" butterfly:", RawPointer(butterfly), " (base:", RawPointer(butterflyBase), ")"); 399 } 400 } 401 402 dataLog(" in ", cycle.scope, " GC[", cycleIndex, "] in '", list.name(), "' list in VM ", 403 RawPointer(vm), " recorded at time ", profile.timestamp(), "\n"); 404 if (profile.stackTrace()) 405 dataLog(*profile.stackTrace()); 406 } 407 408 void HeapVerifier::checkIfRecorded(HeapCell* cell) 198 409 { 199 410 bool found = false; 411 const char* const prefix = " "; 412 static const bool verbose = true; 200 413 201 414 for (int cycleIndex = 0; cycleIndex > -m_numberOfCycles; cycleIndex--) { 202 415 GCCycle& cycle = cycleForIndex(cycleIndex); 203 CellList& beforeList = cycle.before; 204 CellList& afterList = cycle.after; 205 206 CellProfile* profile; 207 profile = beforeList.findCell(cell); 208 if (profile) { 209 reportCell(*profile, cycleIndex, cycle, beforeList); 210 found = true; 211 } 212 profile = afterList.findCell(cell); 213 if (profile) { 214 reportCell(*profile, cycleIndex, cycle, afterList); 215 found = true; 416 CellList* lists[] = { &cycle.before, &cycle.after }; 417 418 if (verbose) 419 dataLog("Checking ", cycle.scope, " GC<", cycle.timestamp, ">, cycle [", cycleIndex, "]:\n"); 420 421 const char* resultPrefix = " "; 422 for (auto* list : lists) { 423 if (verbose) 424 dataLog(prefix, "Cycle [", cycleIndex, "] '", list->name(), "' list: "); 425 426 CellProfile* profile = list->find(cell); 427 if (profile) { 428 reportCell(*profile, cycleIndex, cycle, *list, resultPrefix); 429 found = true; 430 } else if (verbose) 431 dataLog(resultPrefix, "cell NOT found\n"); 216 432 } 217 433 } 218 434 219 435 if (!found) 220 dataLogF("cell %p NOT FOUND\n", cell); 436 dataLog(prefix, "cell ", RawPointer(cell), " NOT FOUND\n"); 437 } 438 439 // The following are slower but more robust versions of the corresponding functions of the same name. 440 // These robust versions are designed so that we can call them interactively from a C++ debugger 441 // to query if a candidate is recorded cell. 442 443 void HeapVerifier::checkIfRecorded(uintptr_t candidateCell) 444 { 445 HeapCell* candidateHeapCell = reinterpret_cast<HeapCell*>(candidateCell); 446 447 VMInspector& inspector = VMInspector::instance(); 448 auto expectedLocker = inspector.lock(Seconds(2)); 449 if (!expectedLocker) { 450 ASSERT(expectedLocker.error() == VMInspector::Error::TimedOut); 451 dataLog("ERROR: Timed out while waiting to iterate VMs."); 452 return; 453 } 454 455 auto& locker = expectedLocker.value(); 456 inspector.iterate(locker, [&] (VM& vm) { 457 if (!vm.heap.m_verifier) 458 return VMInspector::FunctorStatus::Continue; 459 460 auto* verifier = vm.heap.m_verifier.get(); 461 dataLog("Search for cell ", RawPointer(candidateHeapCell), " in VM ", RawPointer(&vm), ":\n"); 462 verifier->checkIfRecorded(candidateHeapCell); 463 return VMInspector::FunctorStatus::Continue; 464 }); 221 465 } 222 466 -
trunk/Source/JavaScriptCore/tools/HeapVerifier.h
r213675 r213883 1 1 /* 2 * Copyright (C) 2014-201 5Apple Inc. All rights reserved.2 * Copyright (C) 2014-2017 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 28 28 #include "CellList.h" 29 29 #include "Heap.h" 30 #include <wtf/MonotonicTime.h> 30 31 31 32 namespace JSC { … … 46 47 HeapVerifier(Heap*, unsigned numberOfGCCyclesToRecord); 47 48 48 void initializeGCCycle(); 49 void startGC(); 50 void endGC(); 51 49 52 void gatherLiveCells(Phase); 50 53 void trimDeadCells(); 51 54 void verify(Phase); 52 55 56 static const char* phaseName(Phase); 57 53 58 // Scans all previously recorded CellLists and checks if the specified 54 59 // cell was in any of those lists. 55 JS_EXPORT_PRIVATE void checkIfRecorded(JSCell*);60 JS_EXPORT_PRIVATE static void checkIfRecorded(uintptr_t maybeCell); 56 61 57 static const char* phaseName(Phase); 62 // Returns false if anything is found to be inconsistent/incorrect about the specified cell. 63 JS_EXPORT_PRIVATE static bool validateCell(HeapCell*, VM* expectedVM = nullptr); 58 64 59 65 private: … … 65 71 } 66 72 73 void reset() 74 { 75 before.reset(); 76 after.reset(); 77 } 78 67 79 CollectionScope scope; 80 MonotonicTime timestamp; 68 81 CellList before; 69 82 CellList after; … … 83 96 84 97 CellList* cellListForGathering(Phase); 85 bool verifyButterflyIsInStorageSpace(Phase, CellList&); 98 bool verifyCellList(Phase, CellList&); 99 static bool validateJSCell(VM* expectedVM, JSCell*, CellProfile*, CellList*, std::function<void()> printHeaderIfNeeded, const char* prefix = ""); 86 100 87 static void reportCell(CellProfile&, int cycleIndex, HeapVerifier::GCCycle&, CellList&); 101 void printVerificationHeader(); 102 103 void checkIfRecorded(HeapCell* maybeHeapCell); 104 void reportCell(CellProfile&, int cycleIndex, HeapVerifier::GCCycle&, CellList&, const char* prefix = nullptr); 88 105 89 106 Heap* m_heap; 90 107 int m_currentCycle; 91 108 int m_numberOfCycles; 109 bool m_didPrintLogs { false }; 92 110 std::unique_ptr<GCCycle[]> m_cycles; 93 111 };
Note: See TracChangeset
for help on using the changeset viewer.