blob: 153881fb9c1e7556c7b42c0f3aca4f76b65185dd [file] [log] [blame]
/*
* Copyright (C) 2014, 2016 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 "HeapIterationScope.h"
#include "JSCInlines.h"
#include "JSObject.h"
#include "MarkedSpaceInlines.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.
}
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()(HeapCell* cell, HeapCell::Kind kind) const
{
if (kind == HeapCell::JSCell) {
// FIXME: This const_cast exists because this isn't a C++ lambda.
// https://bugs.webkit.org/show_bug.cgi?id=159644
const_cast<GatherLiveObjFunctor*>(this)->visit(static_cast<JSCell*>(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 &currentCycle().before;
case Phase::AfterMarking:
return &currentCycle().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, LiveObjectList&)
{
// FIXME: Make this work again. https://bugs.webkit.org/show_bug.cgi?id=161752
return true;
}
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 = butterfly->base(structure);
dataLogF("FOUND obj %p type '%s' butterfly %p (base %p) in GC[%d] %s list '%s'\n",
obj, structure->classInfo()->className,
butterfly, butterflyBase,
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