2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "HeapVerifier.h"
29 #include "ButterflyInlines.h"
30 #include "CopiedSpaceInlines.h"
31 #include "HeapIterationScope.h"
32 #include "JSCInlines.h"
37 LiveObjectData
* LiveObjectList::findObject(JSObject
* obj
)
39 for (size_t i
= 0; i
< liveObjects
.size(); i
++) {
40 LiveObjectData
& data
= liveObjects
[i
];
47 HeapVerifier::HeapVerifier(Heap
* heap
, unsigned numberOfGCCyclesToRecord
)
50 , m_numberOfCycles(numberOfGCCyclesToRecord
)
52 RELEASE_ASSERT(m_numberOfCycles
> 0);
53 m_cycles
= std::make_unique
<GCCycle
[]>(m_numberOfCycles
);
56 const char* HeapVerifier::collectionTypeName(HeapOperation type
)
62 return "AnyCollection";
66 return "EdenCollection";
68 return "FullCollection";
70 RELEASE_ASSERT_NOT_REACHED();
71 return nullptr; // Silencing a compiler warning.
74 const char* HeapVerifier::phaseName(HeapVerifier::Phase phase
)
79 case Phase::BeforeMarking
:
80 return "BeforeMarking";
81 case Phase::AfterMarking
:
82 return "AfterMarking";
86 RELEASE_ASSERT_NOT_REACHED();
87 return nullptr; // Silencing a compiler warning.
90 static void getButterflyDetails(JSObject
* obj
, void*& butterflyBase
, size_t& butterflyCapacityInBytes
, CopiedBlock
*& butterflyBlock
)
92 Structure
* structure
= obj
->structure();
93 Butterfly
* butterfly
= obj
->butterfly();
94 butterflyBase
= butterfly
->base(structure
);
95 butterflyBlock
= CopiedSpace::blockFor(butterflyBase
);
97 size_t propertyCapacity
= structure
->outOfLineCapacity();
99 size_t indexingPayloadSizeInBytes
;
100 bool hasIndexingHeader
= obj
->hasIndexingHeader();
101 if (UNLIKELY(hasIndexingHeader
)) {
102 preCapacity
= butterfly
->indexingHeader()->preCapacity(structure
);
103 indexingPayloadSizeInBytes
= butterfly
->indexingHeader()->indexingPayloadSizeInBytes(structure
);
106 indexingPayloadSizeInBytes
= 0;
108 butterflyCapacityInBytes
= Butterfly::totalSize(preCapacity
, propertyCapacity
, hasIndexingHeader
, indexingPayloadSizeInBytes
);
111 void HeapVerifier::initializeGCCycle()
115 currentCycle().collectionType
= heap
->operationInProgress();
118 struct GatherLiveObjFunctor
: MarkedBlock::CountFunctor
{
119 GatherLiveObjFunctor(LiveObjectList
& list
)
122 ASSERT(!list
.liveObjects
.size());
125 inline void visit(JSCell
* cell
)
127 if (!cell
->isObject())
129 LiveObjectData
data(asObject(cell
));
130 m_list
.liveObjects
.append(data
);
133 IterationStatus
operator()(JSCell
* cell
)
136 return IterationStatus::Continue
;
139 LiveObjectList
& m_list
;
142 void HeapVerifier::gatherLiveObjects(HeapVerifier::Phase phase
)
145 LiveObjectList
& list
= *liveObjectListForGathering(phase
);
147 HeapIterationScope
iterationScope(*heap
);
149 GatherLiveObjFunctor
functor(list
);
150 heap
->m_objectSpace
.forEachLiveCell(iterationScope
, functor
);
153 LiveObjectList
* HeapVerifier::liveObjectListForGathering(HeapVerifier::Phase phase
)
156 case Phase::BeforeMarking
:
157 return ¤tCycle().before
;
158 case Phase::AfterMarking
:
159 return ¤tCycle().after
;
160 case Phase::BeforeGC
:
162 // We should not be gathering live objects during these phases.
165 RELEASE_ASSERT_NOT_REACHED();
166 return nullptr; // Silencing a compiler warning.
169 static void trimDeadObjectsFromList(HashSet
<JSObject
*>& knownLiveSet
, LiveObjectList
& list
)
171 if (!list
.hasLiveObjects
)
174 size_t liveObjectsFound
= 0;
175 for (size_t i
= 0; i
< list
.liveObjects
.size(); i
++) {
176 LiveObjectData
& objData
= list
.liveObjects
[i
];
177 if (objData
.isConfirmedDead
)
178 continue; // Don't "resurrect" known dead objects.
179 if (!knownLiveSet
.contains(objData
.obj
)) {
180 objData
.isConfirmedDead
= true;
185 list
.hasLiveObjects
= !!liveObjectsFound
;
188 void HeapVerifier::trimDeadObjects()
190 HashSet
<JSObject
*> knownLiveSet
;
192 LiveObjectList
& after
= currentCycle().after
;
193 for (size_t i
= 0; i
< after
.liveObjects
.size(); i
++) {
194 LiveObjectData
& objData
= after
.liveObjects
[i
];
195 knownLiveSet
.add(objData
.obj
);
198 trimDeadObjectsFromList(knownLiveSet
, currentCycle().before
);
200 for (int i
= -1; i
> -m_numberOfCycles
; i
--) {
201 trimDeadObjectsFromList(knownLiveSet
, cycleForIndex(i
).before
);
202 trimDeadObjectsFromList(knownLiveSet
, cycleForIndex(i
).after
);
206 bool HeapVerifier::verifyButterflyIsInStorageSpace(Phase phase
, LiveObjectList
& list
)
208 auto& liveObjects
= list
.liveObjects
;
210 CopiedSpace
& storageSpace
= m_heap
->m_storageSpace
;
211 bool listNamePrinted
= false;
213 for (size_t i
= 0; i
< liveObjects
.size(); i
++) {
214 LiveObjectData
& objectData
= liveObjects
[i
];
215 if (objectData
.isConfirmedDead
)
218 JSObject
* obj
= objectData
.obj
;
219 Butterfly
* butterfly
= obj
->butterfly();
222 size_t butterflyCapacityInBytes
;
223 CopiedBlock
* butterflyBlock
;
224 getButterflyDetails(obj
, butterflyBase
, butterflyCapacityInBytes
, butterflyBlock
);
226 if (!storageSpace
.contains(butterflyBlock
)) {
227 if (!listNamePrinted
) {
228 dataLogF("Verification @ phase %s FAILED in object list '%s' (size %zu)\n",
229 phaseName(phase
), list
.name
, liveObjects
.size());
230 listNamePrinted
= true;
233 Structure
* structure
= obj
->structure();
234 const char* structureClassName
= structure
->classInfo()->className
;
235 dataLogF(" butterfly %p (base %p size %zu block %p) NOT in StorageSpace | obj %p type '%s'\n",
236 butterfly
, butterflyBase
, butterflyCapacityInBytes
, butterflyBlock
, obj
, structureClassName
);
244 void HeapVerifier::verify(HeapVerifier::Phase phase
)
246 bool beforeVerified
= verifyButterflyIsInStorageSpace(phase
, currentCycle().before
);
247 bool afterVerified
= verifyButterflyIsInStorageSpace(phase
, currentCycle().after
);
248 RELEASE_ASSERT(beforeVerified
&& afterVerified
);
251 void HeapVerifier::reportObject(LiveObjectData
& objData
, int cycleIndex
, HeapVerifier::GCCycle
& cycle
, LiveObjectList
& list
)
253 JSObject
* obj
= objData
.obj
;
255 if (objData
.isConfirmedDead
) {
256 dataLogF("FOUND dead obj %p in GC[%d] %s list '%s'\n",
257 obj
, cycleIndex
, cycle
.collectionTypeName(), list
.name
);
261 Structure
* structure
= obj
->structure();
262 Butterfly
* butterfly
= obj
->butterfly();
264 size_t butterflyCapacityInBytes
;
265 CopiedBlock
* butterflyBlock
;
266 getButterflyDetails(obj
, butterflyBase
, butterflyCapacityInBytes
, butterflyBlock
);
268 dataLogF("FOUND obj %p type '%s' butterfly %p (base %p size %zu block %p) in GC[%d] %s list '%s'\n",
269 obj
, structure
->classInfo()->className
,
270 butterfly
, butterflyBase
, butterflyCapacityInBytes
, butterflyBlock
,
271 cycleIndex
, cycle
.collectionTypeName(), list
.name
);
274 void HeapVerifier::checkIfRecorded(JSObject
* obj
)
278 for (int cycleIndex
= 0; cycleIndex
> -m_numberOfCycles
; cycleIndex
--) {
279 GCCycle
& cycle
= cycleForIndex(cycleIndex
);
280 LiveObjectList
& beforeList
= cycle
.before
;
281 LiveObjectList
& afterList
= cycle
.after
;
283 LiveObjectData
* objData
;
284 objData
= beforeList
.findObject(obj
);
286 reportObject(*objData
, cycleIndex
, cycle
, beforeList
);
289 objData
= afterList
.findObject(obj
);
291 reportObject(*objData
, cycleIndex
, cycle
, afterList
);
297 dataLogF("obj %p NOT FOUND\n", obj
);