1/*
2 * Copyright (C) 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26#include "config.h"
27#include "HeapVerifier.h"
28
29#include "ButterflyInlines.h"
30#include "CopiedSpaceInlines.h"
31#include "HeapIterationScope.h"
32#include "JSCInlines.h"
33#include "JSObject.h"
34
35namespace JSC {
36
37HeapVerifier::HeapVerifier(Heap* heap, unsigned numberOfGCCyclesToRecord)
38 : m_heap(heap)
39 , m_currentCycle(0)
40 , m_numberOfCycles(numberOfGCCyclesToRecord)
41{
42 RELEASE_ASSERT(m_numberOfCycles > 0);
43 m_cycles = std::make_unique<GCCycle[]>(m_numberOfCycles);
44}
45
46const char* HeapVerifier::collectionTypeName(HeapOperation type)
47{
48 switch (type) {
49 case NoOperation:
50 return "NoOperation";
51 case AnyCollection:
52 return "AnyCollection";
53 case Allocation:
54 return "Allocation";
55 case EdenCollection:
56 return "EdenCollection";
57 case FullCollection:
58 return "FullCollection";
59 }
60 RELEASE_ASSERT_NOT_REACHED();
61 return nullptr; // Silencing a compiler warning.
62}
63
64const char* HeapVerifier::phaseName(HeapVerifier::Phase phase)
65{
66 switch (phase) {
67 case Phase::BeforeGC:
68 return "BeforeGC";
69 case Phase::BeforeMarking:
70 return "BeforeMarking";
71 case Phase::AfterMarking:
72 return "AfterMarking";
73 case Phase::AfterGC:
74 return "AfterGC";
75 }
76 RELEASE_ASSERT_NOT_REACHED();
77 return nullptr; // Silencing a compiler warning.
78}
79
80static void getButterflyDetails(JSObject* obj, void*& butterflyBase, size_t& butterflyCapacityInBytes, CopiedBlock*& butterflyBlock)
81{
82 Structure* structure = obj->structure();
83 Butterfly* butterfly = obj->butterfly();
84 butterflyBase = butterfly->base(structure);
85 butterflyBlock = CopiedSpace::blockFor(butterflyBase);
86
87 size_t propertyCapacity = structure->outOfLineCapacity();
88 size_t preCapacity;
89 size_t indexingPayloadSizeInBytes;
90 bool hasIndexingHeader = obj->hasIndexingHeader();
91 if (UNLIKELY(hasIndexingHeader)) {
92 preCapacity = butterfly->indexingHeader()->preCapacity(structure);
93 indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
94 } else {
95 preCapacity = 0;
96 indexingPayloadSizeInBytes = 0;
97 }
98 butterflyCapacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
99}
100
101void HeapVerifier::initializeGCCycle()
102{
103 Heap* heap = m_heap;
104 incrementCycle();
105 currentCycle().collectionType = heap->operationInProgress();
106}
107
108struct GatherLiveObjFunctor : MarkedBlock::CountFunctor {
109 GatherLiveObjFunctor(LiveObjectList& list)
110 : m_list(list)
111 {
112 ASSERT(!list.liveObjects.size());
113 }
114
115 inline void visit(JSCell* cell)
116 {
117 if (!cell->isObject())
118 return;
119 LiveObjectData data(asObject(cell));
120 m_list.liveObjects.append(data);
121 }
122
123 IterationStatus operator()(JSCell* cell)
124 {
125 visit(cell);
126 return IterationStatus::Continue;
127 }
128
129 LiveObjectList& m_list;
130};
131
132void HeapVerifier::gatherLiveObjects(HeapVerifier::Phase phase)
133{
134 Heap* heap = m_heap;
135 LiveObjectList& list = *liveObjectListForGathering(phase);
136
137 HeapIterationScope iterationScope(*heap);
138 list.reset();
139 GatherLiveObjFunctor functor(list);
140 heap->m_objectSpace.forEachLiveCell(iterationScope, functor);
141}
142
143LiveObjectList* HeapVerifier::liveObjectListForGathering(HeapVerifier::Phase phase)
144{
145 switch (phase) {
146 case Phase::BeforeMarking:
147 return &currentCycle().before;
148 case Phase::AfterMarking:
149 return &currentCycle().after;
150 case Phase::BeforeGC:
151 case Phase::AfterGC:
152 // We should not be gathering live objects during these phases.
153 break;
154 }
155 RELEASE_ASSERT_NOT_REACHED();
156 return nullptr; // Silencing a compiler warning.
157}
158
159static void trimDeadObjectsFromList(HashSet<JSObject*>& knownLiveSet, LiveObjectList& list)
160{
161 if (!list.hasLiveObjects)
162 return;
163
164 size_t liveObjectsFound = 0;
165 for (size_t i = 0; i < list.liveObjects.size(); i++) {
166 LiveObjectData& objData = list.liveObjects[i];
167 if (objData.isConfirmedDead)
168 continue; // Don't "resurrect" known dead objects.
169 if (!knownLiveSet.contains(objData.obj)) {
170 objData.isConfirmedDead = true;
171 continue;
172 }
173 liveObjectsFound++;
174 }
175 list.hasLiveObjects = !!liveObjectsFound;
176}
177
178void HeapVerifier::trimDeadObjects()
179{
180 HashSet<JSObject*> knownLiveSet;
181
182 LiveObjectList& after = currentCycle().after;
183 for (size_t i = 0; i < after.liveObjects.size(); i++) {
184 LiveObjectData& objData = after.liveObjects[i];
185 knownLiveSet.add(objData.obj);
186 }
187
188 trimDeadObjectsFromList(knownLiveSet, currentCycle().before);
189
190 for (int i = -1; i > -m_numberOfCycles; i--) {
191 trimDeadObjectsFromList(knownLiveSet, cycleForIndex(i).before);
192 trimDeadObjectsFromList(knownLiveSet, cycleForIndex(i).after);
193 }
194}
195
196bool HeapVerifier::verifyButterflyIsInStorageSpace(Phase phase, LiveObjectList& list)
197{
198 auto& liveObjects = list.liveObjects;
199
200 CopiedSpace& storageSpace = m_heap->m_storageSpace;
201 bool listNamePrinted = false;
202 bool success = true;
203 for (size_t i = 0; i < liveObjects.size(); i++) {
204 LiveObjectData& objectData = liveObjects[i];
205 if (objectData.isConfirmedDead)
206 continue;
207
208 JSObject* obj = objectData.obj;
209 Butterfly* butterfly = obj->butterfly();
210 if (butterfly) {
211 void* butterflyBase;
212 size_t butterflyCapacityInBytes;
213 CopiedBlock* butterflyBlock;
214 getButterflyDetails(obj, butterflyBase, butterflyCapacityInBytes, butterflyBlock);
215
216 if (!storageSpace.contains(butterflyBlock)) {
217 if (!listNamePrinted) {
218 dataLogF("Verification @ phase %s FAILED in object list '%s' (size %zu)\n",
219 phaseName(phase), list.name, liveObjects.size());
220 listNamePrinted = true;
221 }
222
223 Structure* structure = obj->structure();
224 const char* structureClassName = structure->classInfo()->className;
225 dataLogF(" butterfly %p (base %p size %zu block %p) NOT in StorageSpace | obj %p type '%s'\n",
226 butterfly, butterflyBase, butterflyCapacityInBytes, butterflyBlock, obj, structureClassName);
227 success = false;
228 }
229 }
230 }
231 return success;
232}
233
234void HeapVerifier::verify(HeapVerifier::Phase phase)
235{
236 bool beforeVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().before);
237 bool afterVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().after);
238 RELEASE_ASSERT(beforeVerified && afterVerified);
239}
240
241void HeapVerifier::reportObject(LiveObjectData& objData, int cycleIndex, HeapVerifier::GCCycle& cycle, LiveObjectList& list)
242{
243 JSObject* obj = objData.obj;
244
245 if (objData.isConfirmedDead) {
246 dataLogF("FOUND dead obj %p in GC[%d] %s list '%s'\n",
247 obj, cycleIndex, cycle.collectionTypeName(), list.name);
248 return;
249 }
250
251 Structure* structure = obj->structure();
252 Butterfly* butterfly = obj->butterfly();
253 void* butterflyBase;
254 size_t butterflyCapacityInBytes;
255 CopiedBlock* butterflyBlock;
256 getButterflyDetails(obj, butterflyBase, butterflyCapacityInBytes, butterflyBlock);
257
258 dataLogF("FOUND obj %p type '%s' butterfly %p (base %p size %zu block %p) in GC[%d] %s list '%s'\n",
259 obj, structure->classInfo()->className,
260 butterfly, butterflyBase, butterflyCapacityInBytes, butterflyBlock,
261 cycleIndex, cycle.collectionTypeName(), list.name);
262}
263
264void HeapVerifier::checkIfRecorded(JSObject* obj)
265{
266 bool found = false;
267
268 for (int cycleIndex = 0; cycleIndex > -m_numberOfCycles; cycleIndex--) {
269 GCCycle& cycle = cycleForIndex(cycleIndex);
270 LiveObjectList& beforeList = cycle.before;
271 LiveObjectList& afterList = cycle.after;
272
273 LiveObjectData* objData;
274 objData = beforeList.findObject(obj);
275 if (objData) {
276 reportObject(*objData, cycleIndex, cycle, beforeList);
277 found = true;
278 }
279 objData = afterList.findObject(obj);
280 if (objData) {
281 reportObject(*objData, cycleIndex, cycle, afterList);
282 found = true;
283 }
284 }
285
286 if (!found)
287 dataLogF("obj %p NOT FOUND\n", obj);
288}
289
290} // namespace JSC
291