diff options
Diffstat (limited to 'Source/JavaScriptCore/heap/HeapVerifier.cpp')
-rw-r--r-- | Source/JavaScriptCore/heap/HeapVerifier.cpp | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/heap/HeapVerifier.cpp b/Source/JavaScriptCore/heap/HeapVerifier.cpp new file mode 100644 index 000000000..0f4e28277 --- /dev/null +++ b/Source/JavaScriptCore/heap/HeapVerifier.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HeapVerifier.h" + +#include "ButterflyInlines.h" +#include "CopiedSpaceInlines.h" +#include "HeapIterationScope.h" +#include "JSCInlines.h" +#include "JSObject.h" + +namespace JSC { + +HeapVerifier::HeapVerifier(Heap* heap, unsigned numberOfGCCyclesToRecord) + : m_heap(heap) + , m_currentCycle(0) + , m_numberOfCycles(numberOfGCCyclesToRecord) +{ + RELEASE_ASSERT(m_numberOfCycles > 0); + m_cycles = std::make_unique<GCCycle[]>(m_numberOfCycles); +} + +const char* HeapVerifier::collectionTypeName(HeapOperation type) +{ + switch (type) { + case NoOperation: + return "NoOperation"; + case AnyCollection: + return "AnyCollection"; + case Allocation: + return "Allocation"; + case EdenCollection: + return "EdenCollection"; + case FullCollection: + return "FullCollection"; + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; // Silencing a compiler warning. +} + +const char* HeapVerifier::phaseName(HeapVerifier::Phase phase) +{ + switch (phase) { + case Phase::BeforeGC: + return "BeforeGC"; + case Phase::BeforeMarking: + return "BeforeMarking"; + case Phase::AfterMarking: + return "AfterMarking"; + case Phase::AfterGC: + return "AfterGC"; + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; // Silencing a compiler warning. +} + +static void getButterflyDetails(JSObject* obj, void*& butterflyBase, size_t& butterflyCapacityInBytes, CopiedBlock*& butterflyBlock) +{ + Structure* structure = obj->structure(); + Butterfly* butterfly = obj->butterfly(); + butterflyBase = butterfly->base(structure); + butterflyBlock = CopiedSpace::blockFor(butterflyBase); + + size_t propertyCapacity = structure->outOfLineCapacity(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = obj->hasIndexingHeader(); + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); + } else { + preCapacity = 0; + indexingPayloadSizeInBytes = 0; + } + butterflyCapacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); +} + +void HeapVerifier::initializeGCCycle() +{ + Heap* heap = m_heap; + incrementCycle(); + currentCycle().collectionType = heap->operationInProgress(); +} + +struct GatherLiveObjFunctor : MarkedBlock::CountFunctor { + GatherLiveObjFunctor(LiveObjectList& list) + : m_list(list) + { + ASSERT(!list.liveObjects.size()); + } + + inline void visit(JSCell* cell) + { + if (!cell->isObject()) + return; + LiveObjectData data(asObject(cell)); + m_list.liveObjects.append(data); + } + + IterationStatus operator()(JSCell* cell) + { + visit(cell); + return IterationStatus::Continue; + } + + LiveObjectList& m_list; +}; + +void HeapVerifier::gatherLiveObjects(HeapVerifier::Phase phase) +{ + Heap* heap = m_heap; + LiveObjectList& list = *liveObjectListForGathering(phase); + + HeapIterationScope iterationScope(*heap); + list.reset(); + GatherLiveObjFunctor functor(list); + heap->m_objectSpace.forEachLiveCell(iterationScope, functor); +} + +LiveObjectList* HeapVerifier::liveObjectListForGathering(HeapVerifier::Phase phase) +{ + switch (phase) { + case Phase::BeforeMarking: + return ¤tCycle().before; + case Phase::AfterMarking: + return ¤tCycle().after; + case Phase::BeforeGC: + case Phase::AfterGC: + // We should not be gathering live objects during these phases. + break; + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; // Silencing a compiler warning. +} + +static void trimDeadObjectsFromList(HashSet<JSObject*>& knownLiveSet, LiveObjectList& list) +{ + if (!list.hasLiveObjects) + return; + + size_t liveObjectsFound = 0; + for (size_t i = 0; i < list.liveObjects.size(); i++) { + LiveObjectData& objData = list.liveObjects[i]; + if (objData.isConfirmedDead) + continue; // Don't "resurrect" known dead objects. + if (!knownLiveSet.contains(objData.obj)) { + objData.isConfirmedDead = true; + continue; + } + liveObjectsFound++; + } + list.hasLiveObjects = !!liveObjectsFound; +} + +void HeapVerifier::trimDeadObjects() +{ + HashSet<JSObject*> knownLiveSet; + + LiveObjectList& after = currentCycle().after; + for (size_t i = 0; i < after.liveObjects.size(); i++) { + LiveObjectData& objData = after.liveObjects[i]; + knownLiveSet.add(objData.obj); + } + + trimDeadObjectsFromList(knownLiveSet, currentCycle().before); + + for (int i = -1; i > -m_numberOfCycles; i--) { + trimDeadObjectsFromList(knownLiveSet, cycleForIndex(i).before); + trimDeadObjectsFromList(knownLiveSet, cycleForIndex(i).after); + } +} + +bool HeapVerifier::verifyButterflyIsInStorageSpace(Phase phase, LiveObjectList& list) +{ + auto& liveObjects = list.liveObjects; + + CopiedSpace& storageSpace = m_heap->m_storageSpace; + bool listNamePrinted = false; + bool success = true; + for (size_t i = 0; i < liveObjects.size(); i++) { + LiveObjectData& objectData = liveObjects[i]; + if (objectData.isConfirmedDead) + continue; + + JSObject* obj = objectData.obj; + Butterfly* butterfly = obj->butterfly(); + if (butterfly) { + void* butterflyBase; + size_t butterflyCapacityInBytes; + CopiedBlock* butterflyBlock; + getButterflyDetails(obj, butterflyBase, butterflyCapacityInBytes, butterflyBlock); + + if (!storageSpace.contains(butterflyBlock)) { + if (!listNamePrinted) { + dataLogF("Verification @ phase %s FAILED in object list '%s' (size %zu)\n", + phaseName(phase), list.name, liveObjects.size()); + listNamePrinted = true; + } + + Structure* structure = obj->structure(); + const char* structureClassName = structure->classInfo()->className; + dataLogF(" butterfly %p (base %p size %zu block %p) NOT in StorageSpace | obj %p type '%s'\n", + butterfly, butterflyBase, butterflyCapacityInBytes, butterflyBlock, obj, structureClassName); + success = false; + } + } + } + return success; +} + +void HeapVerifier::verify(HeapVerifier::Phase phase) +{ + bool beforeVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().before); + bool afterVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().after); + RELEASE_ASSERT(beforeVerified && afterVerified); +} + +void HeapVerifier::reportObject(LiveObjectData& objData, int cycleIndex, HeapVerifier::GCCycle& cycle, LiveObjectList& list) +{ + JSObject* obj = objData.obj; + + if (objData.isConfirmedDead) { + dataLogF("FOUND dead obj %p in GC[%d] %s list '%s'\n", + obj, cycleIndex, cycle.collectionTypeName(), list.name); + return; + } + + Structure* structure = obj->structure(); + Butterfly* butterfly = obj->butterfly(); + void* butterflyBase; + size_t butterflyCapacityInBytes; + CopiedBlock* butterflyBlock; + getButterflyDetails(obj, butterflyBase, butterflyCapacityInBytes, butterflyBlock); + + dataLogF("FOUND obj %p type '%s' butterfly %p (base %p size %zu block %p) in GC[%d] %s list '%s'\n", + obj, structure->classInfo()->className, + butterfly, butterflyBase, butterflyCapacityInBytes, butterflyBlock, + cycleIndex, cycle.collectionTypeName(), list.name); +} + +void HeapVerifier::checkIfRecorded(JSObject* obj) +{ + bool found = false; + + for (int cycleIndex = 0; cycleIndex > -m_numberOfCycles; cycleIndex--) { + GCCycle& cycle = cycleForIndex(cycleIndex); + LiveObjectList& beforeList = cycle.before; + LiveObjectList& afterList = cycle.after; + + LiveObjectData* objData; + objData = beforeList.findObject(obj); + if (objData) { + reportObject(*objData, cycleIndex, cycle, beforeList); + found = true; + } + objData = afterList.findObject(obj); + if (objData) { + reportObject(*objData, cycleIndex, cycle, afterList); + found = true; + } + } + + if (!found) + dataLogF("obj %p NOT FOUND\n", obj); +} + +} // namespace JSC |