]> git.saurik.com Git - apple/javascriptcore.git/blob - heap/HeapVerifier.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / heap / HeapVerifier.cpp
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
35 namespace JSC {
36
37 LiveObjectData* LiveObjectList::findObject(JSObject* obj)
38 {
39 for (size_t i = 0; i < liveObjects.size(); i++) {
40 LiveObjectData& data = liveObjects[i];
41 if (obj == data.obj)
42 return &data;
43 }
44 return nullptr;
45 }
46
47 HeapVerifier::HeapVerifier(Heap* heap, unsigned numberOfGCCyclesToRecord)
48 : m_heap(heap)
49 , m_currentCycle(0)
50 , m_numberOfCycles(numberOfGCCyclesToRecord)
51 {
52 RELEASE_ASSERT(m_numberOfCycles > 0);
53 m_cycles = std::make_unique<GCCycle[]>(m_numberOfCycles);
54 }
55
56 const char* HeapVerifier::collectionTypeName(HeapOperation type)
57 {
58 switch (type) {
59 case NoOperation:
60 return "NoOperation";
61 case AnyCollection:
62 return "AnyCollection";
63 case Allocation:
64 return "Allocation";
65 case EdenCollection:
66 return "EdenCollection";
67 case FullCollection:
68 return "FullCollection";
69 }
70 RELEASE_ASSERT_NOT_REACHED();
71 return nullptr; // Silencing a compiler warning.
72 }
73
74 const char* HeapVerifier::phaseName(HeapVerifier::Phase phase)
75 {
76 switch (phase) {
77 case Phase::BeforeGC:
78 return "BeforeGC";
79 case Phase::BeforeMarking:
80 return "BeforeMarking";
81 case Phase::AfterMarking:
82 return "AfterMarking";
83 case Phase::AfterGC:
84 return "AfterGC";
85 }
86 RELEASE_ASSERT_NOT_REACHED();
87 return nullptr; // Silencing a compiler warning.
88 }
89
90 static void getButterflyDetails(JSObject* obj, void*& butterflyBase, size_t& butterflyCapacityInBytes, CopiedBlock*& butterflyBlock)
91 {
92 Structure* structure = obj->structure();
93 Butterfly* butterfly = obj->butterfly();
94 butterflyBase = butterfly->base(structure);
95 butterflyBlock = CopiedSpace::blockFor(butterflyBase);
96
97 size_t propertyCapacity = structure->outOfLineCapacity();
98 size_t preCapacity;
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);
104 } else {
105 preCapacity = 0;
106 indexingPayloadSizeInBytes = 0;
107 }
108 butterflyCapacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
109 }
110
111 void HeapVerifier::initializeGCCycle()
112 {
113 Heap* heap = m_heap;
114 incrementCycle();
115 currentCycle().collectionType = heap->operationInProgress();
116 }
117
118 struct GatherLiveObjFunctor : MarkedBlock::CountFunctor {
119 GatherLiveObjFunctor(LiveObjectList& list)
120 : m_list(list)
121 {
122 ASSERT(!list.liveObjects.size());
123 }
124
125 inline void visit(JSCell* cell)
126 {
127 if (!cell->isObject())
128 return;
129 LiveObjectData data(asObject(cell));
130 m_list.liveObjects.append(data);
131 }
132
133 IterationStatus operator()(JSCell* cell)
134 {
135 visit(cell);
136 return IterationStatus::Continue;
137 }
138
139 LiveObjectList& m_list;
140 };
141
142 void HeapVerifier::gatherLiveObjects(HeapVerifier::Phase phase)
143 {
144 Heap* heap = m_heap;
145 LiveObjectList& list = *liveObjectListForGathering(phase);
146
147 HeapIterationScope iterationScope(*heap);
148 list.reset();
149 GatherLiveObjFunctor functor(list);
150 heap->m_objectSpace.forEachLiveCell(iterationScope, functor);
151 }
152
153 LiveObjectList* HeapVerifier::liveObjectListForGathering(HeapVerifier::Phase phase)
154 {
155 switch (phase) {
156 case Phase::BeforeMarking:
157 return &currentCycle().before;
158 case Phase::AfterMarking:
159 return &currentCycle().after;
160 case Phase::BeforeGC:
161 case Phase::AfterGC:
162 // We should not be gathering live objects during these phases.
163 break;
164 }
165 RELEASE_ASSERT_NOT_REACHED();
166 return nullptr; // Silencing a compiler warning.
167 }
168
169 static void trimDeadObjectsFromList(HashSet<JSObject*>& knownLiveSet, LiveObjectList& list)
170 {
171 if (!list.hasLiveObjects)
172 return;
173
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;
181 continue;
182 }
183 liveObjectsFound++;
184 }
185 list.hasLiveObjects = !!liveObjectsFound;
186 }
187
188 void HeapVerifier::trimDeadObjects()
189 {
190 HashSet<JSObject*> knownLiveSet;
191
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);
196 }
197
198 trimDeadObjectsFromList(knownLiveSet, currentCycle().before);
199
200 for (int i = -1; i > -m_numberOfCycles; i--) {
201 trimDeadObjectsFromList(knownLiveSet, cycleForIndex(i).before);
202 trimDeadObjectsFromList(knownLiveSet, cycleForIndex(i).after);
203 }
204 }
205
206 bool HeapVerifier::verifyButterflyIsInStorageSpace(Phase phase, LiveObjectList& list)
207 {
208 auto& liveObjects = list.liveObjects;
209
210 CopiedSpace& storageSpace = m_heap->m_storageSpace;
211 bool listNamePrinted = false;
212 bool success = true;
213 for (size_t i = 0; i < liveObjects.size(); i++) {
214 LiveObjectData& objectData = liveObjects[i];
215 if (objectData.isConfirmedDead)
216 continue;
217
218 JSObject* obj = objectData.obj;
219 Butterfly* butterfly = obj->butterfly();
220 if (butterfly) {
221 void* butterflyBase;
222 size_t butterflyCapacityInBytes;
223 CopiedBlock* butterflyBlock;
224 getButterflyDetails(obj, butterflyBase, butterflyCapacityInBytes, butterflyBlock);
225
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;
231 }
232
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);
237 success = false;
238 }
239 }
240 }
241 return success;
242 }
243
244 void HeapVerifier::verify(HeapVerifier::Phase phase)
245 {
246 bool beforeVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().before);
247 bool afterVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().after);
248 RELEASE_ASSERT(beforeVerified && afterVerified);
249 }
250
251 void HeapVerifier::reportObject(LiveObjectData& objData, int cycleIndex, HeapVerifier::GCCycle& cycle, LiveObjectList& list)
252 {
253 JSObject* obj = objData.obj;
254
255 if (objData.isConfirmedDead) {
256 dataLogF("FOUND dead obj %p in GC[%d] %s list '%s'\n",
257 obj, cycleIndex, cycle.collectionTypeName(), list.name);
258 return;
259 }
260
261 Structure* structure = obj->structure();
262 Butterfly* butterfly = obj->butterfly();
263 void* butterflyBase;
264 size_t butterflyCapacityInBytes;
265 CopiedBlock* butterflyBlock;
266 getButterflyDetails(obj, butterflyBase, butterflyCapacityInBytes, butterflyBlock);
267
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);
272 }
273
274 void HeapVerifier::checkIfRecorded(JSObject* obj)
275 {
276 bool found = false;
277
278 for (int cycleIndex = 0; cycleIndex > -m_numberOfCycles; cycleIndex--) {
279 GCCycle& cycle = cycleForIndex(cycleIndex);
280 LiveObjectList& beforeList = cycle.before;
281 LiveObjectList& afterList = cycle.after;
282
283 LiveObjectData* objData;
284 objData = beforeList.findObject(obj);
285 if (objData) {
286 reportObject(*objData, cycleIndex, cycle, beforeList);
287 found = true;
288 }
289 objData = afterList.findObject(obj);
290 if (objData) {
291 reportObject(*objData, cycleIndex, cycle, afterList);
292 found = true;
293 }
294 }
295
296 if (!found)
297 dataLogF("obj %p NOT FOUND\n", obj);
298 }
299
300 } // namespace JSC