Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifdef MOZ_VALGRIND
8 : # include <valgrind/memcheck.h>
9 : #endif
10 :
11 : #include "mozilla/DebugOnly.h"
12 : #include "mozilla/IntegerPrintfMacros.h"
13 : #include "mozilla/Sprintf.h"
14 :
15 : #include "jscntxt.h"
16 : #include "jsgc.h"
17 : #include "jsprf.h"
18 :
19 : #include "gc/GCInternals.h"
20 : #include "gc/Zone.h"
21 : #include "js/GCAPI.h"
22 : #include "js/HashTable.h"
23 :
24 : #include "jscntxtinlines.h"
25 : #include "jsgcinlines.h"
26 :
27 : using namespace js;
28 : using namespace js::gc;
29 :
30 : #ifdef JS_GC_ZEAL
31 :
32 : /*
33 : * Write barrier verification
34 : *
35 : * The next few functions are for write barrier verification.
36 : *
37 : * The VerifyBarriers function is a shorthand. It checks if a verification phase
38 : * is currently running. If not, it starts one. Otherwise, it ends the current
39 : * phase and starts a new one.
40 : *
41 : * The user can adjust the frequency of verifications, which causes
42 : * VerifyBarriers to be a no-op all but one out of N calls. However, if the
43 : * |always| parameter is true, it starts a new phase no matter what.
44 : *
45 : * Pre-Barrier Verifier:
46 : * When StartVerifyBarriers is called, a snapshot is taken of all objects in
47 : * the GC heap and saved in an explicit graph data structure. Later,
48 : * EndVerifyBarriers traverses the heap again. Any pointer values that were in
49 : * the snapshot and are no longer found must be marked; otherwise an assertion
50 : * triggers. Note that we must not GC in between starting and finishing a
51 : * verification phase.
52 : */
53 :
54 : struct EdgeValue
55 : {
56 : void* thing;
57 : JS::TraceKind kind;
58 : const char* label;
59 : };
60 :
61 : struct VerifyNode
62 : {
63 : void* thing;
64 : JS::TraceKind kind;
65 : uint32_t count;
66 : EdgeValue edges[1];
67 : };
68 :
69 : typedef HashMap<void*, VerifyNode*, DefaultHasher<void*>, SystemAllocPolicy> NodeMap;
70 :
71 : /*
72 : * The verifier data structures are simple. The entire graph is stored in a
73 : * single block of memory. At the beginning is a VerifyNode for the root
74 : * node. It is followed by a sequence of EdgeValues--the exact number is given
75 : * in the node. After the edges come more nodes and their edges.
76 : *
77 : * The edgeptr and term fields are used to allocate out of the block of memory
78 : * for the graph. If we run out of memory (i.e., if edgeptr goes beyond term),
79 : * we just abandon the verification.
80 : *
81 : * The nodemap field is a hashtable that maps from the address of the GC thing
82 : * to the VerifyNode that represents it.
83 : */
84 : class js::VerifyPreTracer final : public JS::CallbackTracer
85 : {
86 : JS::AutoDisableGenerationalGC noggc;
87 :
88 : void onChild(const JS::GCCellPtr& thing) override;
89 :
90 : public:
91 : /* The gcNumber when the verification began. */
92 : uint64_t number;
93 :
94 : /* This counts up to gcZealFrequency to decide whether to verify. */
95 : int count;
96 :
97 : /* This graph represents the initial GC "snapshot". */
98 : VerifyNode* curnode;
99 : VerifyNode* root;
100 : char* edgeptr;
101 : char* term;
102 : NodeMap nodemap;
103 :
104 0 : explicit VerifyPreTracer(JSRuntime* rt)
105 0 : : JS::CallbackTracer(rt), noggc(TlsContext.get()), number(rt->gc.gcNumber()),
106 0 : count(0), curnode(nullptr), root(nullptr), edgeptr(nullptr), term(nullptr)
107 0 : {}
108 :
109 0 : ~VerifyPreTracer() {
110 0 : js_free(root);
111 0 : }
112 : };
113 :
114 : /*
115 : * This function builds up the heap snapshot by adding edges to the current
116 : * node.
117 : */
118 : void
119 0 : VerifyPreTracer::onChild(const JS::GCCellPtr& thing)
120 : {
121 0 : MOZ_ASSERT(!IsInsideNursery(thing.asCell()));
122 :
123 : // Skip things in other runtimes.
124 0 : if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime())
125 0 : return;
126 :
127 0 : edgeptr += sizeof(EdgeValue);
128 0 : if (edgeptr >= term) {
129 0 : edgeptr = term;
130 0 : return;
131 : }
132 :
133 0 : VerifyNode* node = curnode;
134 0 : uint32_t i = node->count;
135 :
136 0 : node->edges[i].thing = thing.asCell();
137 0 : node->edges[i].kind = thing.kind();
138 0 : node->edges[i].label = contextName();
139 0 : node->count++;
140 : }
141 :
142 : static VerifyNode*
143 0 : MakeNode(VerifyPreTracer* trc, void* thing, JS::TraceKind kind)
144 : {
145 0 : NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
146 0 : if (!p) {
147 0 : VerifyNode* node = (VerifyNode*)trc->edgeptr;
148 0 : trc->edgeptr += sizeof(VerifyNode) - sizeof(EdgeValue);
149 0 : if (trc->edgeptr >= trc->term) {
150 0 : trc->edgeptr = trc->term;
151 0 : return nullptr;
152 : }
153 :
154 0 : node->thing = thing;
155 0 : node->count = 0;
156 0 : node->kind = kind;
157 0 : if (!trc->nodemap.add(p, thing, node)) {
158 0 : trc->edgeptr = trc->term;
159 0 : return nullptr;
160 : }
161 :
162 0 : return node;
163 : }
164 0 : return nullptr;
165 : }
166 :
167 : static VerifyNode*
168 0 : NextNode(VerifyNode* node)
169 : {
170 0 : if (node->count == 0)
171 0 : return (VerifyNode*)((char*)node + sizeof(VerifyNode) - sizeof(EdgeValue));
172 : else
173 : return (VerifyNode*)((char*)node + sizeof(VerifyNode) +
174 0 : sizeof(EdgeValue)*(node->count - 1));
175 : }
176 :
177 : void
178 0 : gc::GCRuntime::startVerifyPreBarriers()
179 : {
180 0 : if (verifyPreData || isIncrementalGCInProgress())
181 0 : return;
182 :
183 0 : if (IsIncrementalGCUnsafe(rt) != AbortReason::None ||
184 0 : TlsContext.get()->keepAtoms ||
185 0 : rt->hasHelperThreadZones() ||
186 0 : rt->cooperatingContexts().length() != 1)
187 : {
188 0 : return;
189 : }
190 :
191 0 : number++;
192 :
193 0 : VerifyPreTracer* trc = js_new<VerifyPreTracer>(rt);
194 0 : if (!trc)
195 0 : return;
196 :
197 0 : AutoPrepareForTracing prep(TlsContext.get(), WithAtoms);
198 :
199 0 : for (auto chunk = allNonEmptyChunks(); !chunk.done(); chunk.next())
200 0 : chunk->bitmap.clear();
201 :
202 0 : gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::TRACE_HEAP);
203 :
204 0 : const size_t size = 64 * 1024 * 1024;
205 0 : trc->root = (VerifyNode*)js_malloc(size);
206 0 : if (!trc->root)
207 0 : goto oom;
208 0 : trc->edgeptr = (char*)trc->root;
209 0 : trc->term = trc->edgeptr + size;
210 :
211 0 : if (!trc->nodemap.init())
212 0 : goto oom;
213 :
214 : /* Create the root node. */
215 0 : trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
216 :
217 0 : incrementalState = State::MarkRoots;
218 :
219 : /* Make all the roots be edges emanating from the root node. */
220 0 : traceRuntime(trc, prep.session().lock);
221 :
222 : VerifyNode* node;
223 0 : node = trc->curnode;
224 0 : if (trc->edgeptr == trc->term)
225 0 : goto oom;
226 :
227 : /* For each edge, make a node for it if one doesn't already exist. */
228 0 : while ((char*)node < trc->edgeptr) {
229 0 : for (uint32_t i = 0; i < node->count; i++) {
230 0 : EdgeValue& e = node->edges[i];
231 0 : VerifyNode* child = MakeNode(trc, e.thing, e.kind);
232 0 : if (child) {
233 0 : trc->curnode = child;
234 0 : js::TraceChildren(trc, e.thing, e.kind);
235 : }
236 0 : if (trc->edgeptr == trc->term)
237 0 : goto oom;
238 : }
239 :
240 0 : node = NextNode(node);
241 : }
242 :
243 0 : verifyPreData = trc;
244 0 : incrementalState = State::Mark;
245 0 : marker.start();
246 :
247 0 : for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
248 0 : MOZ_ASSERT(!zone->usedByHelperThread());
249 0 : zone->setNeedsIncrementalBarrier(true);
250 0 : zone->arenas.purge();
251 : }
252 :
253 0 : return;
254 :
255 : oom:
256 0 : incrementalState = State::NotActive;
257 0 : js_delete(trc);
258 0 : verifyPreData = nullptr;
259 : }
260 :
261 : static bool
262 0 : IsMarkedOrAllocated(TenuredCell* cell)
263 : {
264 0 : return cell->isMarkedAny() || cell->arena()->allocatedDuringIncremental;
265 : }
266 :
267 : struct CheckEdgeTracer : public JS::CallbackTracer {
268 : VerifyNode* node;
269 0 : explicit CheckEdgeTracer(JSRuntime* rt) : JS::CallbackTracer(rt), node(nullptr) {}
270 : void onChild(const JS::GCCellPtr& thing) override;
271 : };
272 :
273 : static const uint32_t MAX_VERIFIER_EDGES = 1000;
274 :
275 : /*
276 : * This function is called by EndVerifyBarriers for every heap edge. If the edge
277 : * already existed in the original snapshot, we "cancel it out" by overwriting
278 : * it with nullptr. EndVerifyBarriers later asserts that the remaining
279 : * non-nullptr edges (i.e., the ones from the original snapshot that must have
280 : * been modified) must point to marked objects.
281 : */
282 : void
283 0 : CheckEdgeTracer::onChild(const JS::GCCellPtr& thing)
284 : {
285 : // Skip things in other runtimes.
286 0 : if (thing.asCell()->asTenured().runtimeFromAnyThread() != runtime())
287 0 : return;
288 :
289 : /* Avoid n^2 behavior. */
290 0 : if (node->count > MAX_VERIFIER_EDGES)
291 0 : return;
292 :
293 0 : for (uint32_t i = 0; i < node->count; i++) {
294 0 : if (node->edges[i].thing == thing.asCell()) {
295 0 : MOZ_ASSERT(node->edges[i].kind == thing.kind());
296 0 : node->edges[i].thing = nullptr;
297 0 : return;
298 : }
299 : }
300 : }
301 :
302 : void
303 10185 : js::gc::AssertSafeToSkipBarrier(TenuredCell* thing)
304 : {
305 20370 : mozilla::DebugOnly<Zone*> zone = thing->zoneFromAnyThread();
306 10185 : MOZ_ASSERT(!zone->needsIncrementalBarrier() || zone->isAtomsZone());
307 10185 : }
308 :
309 : static bool
310 0 : IsMarkedOrAllocated(const EdgeValue& edge)
311 : {
312 0 : if (!edge.thing || IsMarkedOrAllocated(TenuredCell::fromPointer(edge.thing)))
313 0 : return true;
314 :
315 : // Permanent atoms and well-known symbols aren't marked during graph traversal.
316 0 : if (edge.kind == JS::TraceKind::String && static_cast<JSString*>(edge.thing)->isPermanentAtom())
317 0 : return true;
318 0 : if (edge.kind == JS::TraceKind::Symbol && static_cast<JS::Symbol*>(edge.thing)->isWellKnownSymbol())
319 0 : return true;
320 :
321 0 : return false;
322 : }
323 :
324 : void
325 0 : gc::GCRuntime::endVerifyPreBarriers()
326 : {
327 0 : VerifyPreTracer* trc = verifyPreData;
328 :
329 0 : if (!trc)
330 0 : return;
331 :
332 0 : MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
333 :
334 0 : AutoPrepareForTracing prep(rt->activeContextFromOwnThread(), SkipAtoms);
335 :
336 0 : bool compartmentCreated = false;
337 :
338 : /* We need to disable barriers before tracing, which may invoke barriers. */
339 0 : for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
340 0 : if (!zone->needsIncrementalBarrier())
341 0 : compartmentCreated = true;
342 :
343 0 : zone->setNeedsIncrementalBarrier(false);
344 : }
345 :
346 : /*
347 : * We need to bump gcNumber so that the methodjit knows that jitcode has
348 : * been discarded.
349 : */
350 0 : MOZ_ASSERT(trc->number == number);
351 0 : number++;
352 :
353 0 : verifyPreData = nullptr;
354 0 : incrementalState = State::NotActive;
355 :
356 0 : if (!compartmentCreated &&
357 0 : IsIncrementalGCUnsafe(rt) == AbortReason::None &&
358 0 : !TlsContext.get()->keepAtoms &&
359 0 : !rt->hasHelperThreadZones())
360 : {
361 0 : CheckEdgeTracer cetrc(rt);
362 :
363 : /* Start after the roots. */
364 0 : VerifyNode* node = NextNode(trc->root);
365 0 : while ((char*)node < trc->edgeptr) {
366 0 : cetrc.node = node;
367 0 : js::TraceChildren(&cetrc, node->thing, node->kind);
368 :
369 0 : if (node->count <= MAX_VERIFIER_EDGES) {
370 0 : for (uint32_t i = 0; i < node->count; i++) {
371 0 : EdgeValue& edge = node->edges[i];
372 0 : if (!IsMarkedOrAllocated(edge)) {
373 : char msgbuf[1024];
374 0 : SprintfLiteral(msgbuf,
375 : "[barrier verifier] Unmarked edge: %s %p '%s' edge to %s %p",
376 : JS::GCTraceKindToAscii(node->kind), node->thing,
377 : edge.label,
378 0 : JS::GCTraceKindToAscii(edge.kind), edge.thing);
379 0 : MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
380 0 : MOZ_CRASH();
381 : }
382 : }
383 : }
384 :
385 0 : node = NextNode(node);
386 : }
387 : }
388 :
389 0 : marker.reset();
390 0 : marker.stop();
391 :
392 0 : js_delete(trc);
393 : }
394 :
395 : /*** Barrier Verifier Scheduling ***/
396 :
397 : void
398 0 : gc::GCRuntime::verifyPreBarriers()
399 : {
400 0 : if (verifyPreData)
401 0 : endVerifyPreBarriers();
402 : else
403 0 : startVerifyPreBarriers();
404 0 : }
405 :
406 : void
407 0 : gc::VerifyBarriers(JSRuntime* rt, VerifierType type)
408 : {
409 0 : if (type == PreBarrierVerifier)
410 0 : rt->gc.verifyPreBarriers();
411 0 : }
412 :
413 : void
414 526705 : gc::GCRuntime::maybeVerifyPreBarriers(bool always)
415 : {
416 526705 : if (!hasZealMode(ZealMode::VerifierPre))
417 526705 : return;
418 :
419 0 : if (TlsContext.get()->suppressGC)
420 0 : return;
421 :
422 0 : if (verifyPreData) {
423 0 : if (++verifyPreData->count < zealFrequency && !always)
424 0 : return;
425 :
426 0 : endVerifyPreBarriers();
427 : }
428 :
429 0 : startVerifyPreBarriers();
430 : }
431 :
432 : void
433 526705 : js::gc::MaybeVerifyBarriers(JSContext* cx, bool always)
434 : {
435 526705 : GCRuntime* gc = &cx->runtime()->gc;
436 526705 : gc->maybeVerifyPreBarriers(always);
437 526705 : }
438 :
439 : void
440 0 : js::gc::GCRuntime::finishVerifier()
441 : {
442 0 : if (verifyPreData) {
443 0 : js_delete(verifyPreData.ref());
444 0 : verifyPreData = nullptr;
445 : }
446 0 : }
447 :
448 : #endif /* JS_GC_ZEAL */
449 :
450 : #if defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
451 :
452 0 : class HeapCheckTracerBase : public JS::CallbackTracer
453 : {
454 : public:
455 : explicit HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind);
456 : bool init();
457 : bool traceHeap(AutoLockForExclusiveAccess& lock);
458 : virtual void checkCell(Cell* cell) = 0;
459 :
460 : protected:
461 : void dumpCellPath();
462 :
463 0 : Cell* parentCell() {
464 0 : return parentIndex == -1 ? nullptr : stack[parentIndex].thing.asCell();
465 : }
466 :
467 : size_t failures;
468 :
469 : private:
470 : void onChild(const JS::GCCellPtr& thing) override;
471 :
472 : struct WorkItem {
473 0 : WorkItem(JS::GCCellPtr thing, const char* name, int parentIndex)
474 0 : : thing(thing), name(name), parentIndex(parentIndex), processed(false)
475 0 : {}
476 :
477 : JS::GCCellPtr thing;
478 : const char* name;
479 : int parentIndex;
480 : bool processed;
481 : };
482 :
483 : JSRuntime* rt;
484 : bool oom;
485 : HashSet<Cell*, DefaultHasher<Cell*>, SystemAllocPolicy> visited;
486 : Vector<WorkItem, 0, SystemAllocPolicy> stack;
487 : int parentIndex;
488 : };
489 :
490 0 : HeapCheckTracerBase::HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind)
491 : : CallbackTracer(rt, weakTraceKind),
492 : failures(0),
493 : rt(rt),
494 : oom(false),
495 0 : parentIndex(-1)
496 : {
497 : #ifdef DEBUG
498 0 : setCheckEdges(false);
499 : #endif
500 0 : }
501 :
502 : bool
503 0 : HeapCheckTracerBase::init()
504 : {
505 0 : return visited.init();
506 : }
507 :
508 : void
509 0 : HeapCheckTracerBase::onChild(const JS::GCCellPtr& thing)
510 : {
511 0 : Cell* cell = thing.asCell();
512 0 : checkCell(cell);
513 :
514 0 : if (visited.lookup(cell))
515 0 : return;
516 :
517 0 : if (!visited.put(cell)) {
518 0 : oom = true;
519 0 : return;
520 : }
521 :
522 : // Don't trace into GC things owned by another runtime.
523 0 : if (cell->runtimeFromAnyThread() != rt)
524 0 : return;
525 :
526 : // Don't trace into GC in zones being used by helper threads.
527 0 : Zone* zone = thing.is<JSObject>() ? thing.as<JSObject>().zone() : cell->asTenured().zone();
528 0 : if (zone->group() && zone->group()->usedByHelperThread)
529 0 : return;
530 :
531 0 : WorkItem item(thing, contextName(), parentIndex);
532 0 : if (!stack.append(item))
533 0 : oom = true;
534 : }
535 :
536 : bool
537 0 : HeapCheckTracerBase::traceHeap(AutoLockForExclusiveAccess& lock)
538 : {
539 : // The analysis thinks that traceRuntime might GC by calling a GC callback.
540 0 : JS::AutoSuppressGCAnalysis nogc;
541 0 : if (!rt->isBeingDestroyed())
542 0 : rt->gc.traceRuntime(this, lock);
543 :
544 0 : while (!stack.empty() && !oom) {
545 0 : WorkItem item = stack.back();
546 0 : if (item.processed) {
547 0 : stack.popBack();
548 : } else {
549 0 : parentIndex = stack.length() - 1;
550 0 : stack.back().processed = true;
551 0 : TraceChildren(this, item.thing);
552 : }
553 : }
554 :
555 0 : return !oom;
556 : }
557 :
558 : void
559 0 : HeapCheckTracerBase::dumpCellPath()
560 : {
561 0 : const char* name = contextName();
562 0 : for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
563 0 : const WorkItem& parent = stack[index];
564 0 : Cell* cell = parent.thing.asCell();
565 0 : fprintf(stderr, " from %s %p %s edge\n",
566 0 : GCTraceKindToAscii(cell->getTraceKind()), cell, name);
567 0 : name = parent.name;
568 : }
569 0 : fprintf(stderr, " from root %s\n", name);
570 0 : }
571 :
572 : #endif // defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
573 :
574 : #ifdef JSGC_HASH_TABLE_CHECKS
575 :
576 0 : class CheckHeapTracer final : public HeapCheckTracerBase
577 : {
578 : public:
579 : explicit CheckHeapTracer(JSRuntime* rt);
580 : void check(AutoLockForExclusiveAccess& lock);
581 :
582 : private:
583 : void checkCell(Cell* cell) override;
584 : };
585 :
586 0 : CheckHeapTracer::CheckHeapTracer(JSRuntime* rt)
587 0 : : HeapCheckTracerBase(rt, TraceWeakMapKeysValues)
588 0 : {}
589 :
590 : inline static bool
591 0 : IsValidGCThingPointer(Cell* cell)
592 : {
593 0 : return (uintptr_t(cell) & CellAlignMask) == 0;
594 : }
595 :
596 : void
597 0 : CheckHeapTracer::checkCell(Cell* cell)
598 : {
599 0 : if (!IsValidGCThingPointer(cell) || !IsGCThingValidAfterMovingGC(cell)) {
600 0 : failures++;
601 0 : fprintf(stderr, "Bad pointer %p\n", cell);
602 0 : dumpCellPath();
603 : }
604 0 : }
605 :
606 : void
607 0 : CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
608 : {
609 0 : if (!traceHeap(lock))
610 0 : return;
611 :
612 0 : if (failures)
613 0 : fprintf(stderr, "Heap check: %" PRIuSIZE " failure(s)\n", failures);
614 0 : MOZ_RELEASE_ASSERT(failures == 0);
615 : }
616 :
617 : void
618 0 : js::gc::CheckHeapAfterGC(JSRuntime* rt)
619 : {
620 0 : AutoTraceSession session(rt, JS::HeapState::Tracing);
621 0 : CheckHeapTracer tracer(rt);
622 0 : if (tracer.init())
623 0 : tracer.check(session.lock);
624 0 : }
625 :
626 : #endif /* JSGC_HASH_TABLE_CHECKS */
627 :
628 : #ifdef DEBUG
629 :
630 0 : class CheckGrayMarkingTracer final : public HeapCheckTracerBase
631 : {
632 : public:
633 : explicit CheckGrayMarkingTracer(JSRuntime* rt);
634 : bool check(AutoLockForExclusiveAccess& lock);
635 :
636 : private:
637 : void checkCell(Cell* cell) override;
638 : };
639 :
640 0 : CheckGrayMarkingTracer::CheckGrayMarkingTracer(JSRuntime* rt)
641 0 : : HeapCheckTracerBase(rt, DoNotTraceWeakMaps)
642 : {
643 : // Weak gray->black edges are allowed.
644 0 : setTraceWeakEdges(false);
645 0 : }
646 :
647 : void
648 0 : CheckGrayMarkingTracer::checkCell(Cell* cell)
649 : {
650 0 : Cell* parent = parentCell();
651 0 : if (!cell->isTenured() || !parent || !parent->isTenured())
652 0 : return;
653 :
654 0 : TenuredCell* tenuredCell = &cell->asTenured();
655 0 : TenuredCell* tenuredParent = &parent->asTenured();
656 0 : if (tenuredParent->isMarkedBlack() && tenuredCell->isMarkedGray())
657 : {
658 0 : failures++;
659 0 : fprintf(stderr, "Found black to gray edge to %s %p\n",
660 0 : GCTraceKindToAscii(cell->getTraceKind()), cell);
661 0 : dumpCellPath();
662 : }
663 : }
664 :
665 : bool
666 0 : CheckGrayMarkingTracer::check(AutoLockForExclusiveAccess& lock)
667 : {
668 0 : if (!traceHeap(lock))
669 0 : return true; // Ignore failure.
670 :
671 0 : return failures == 0;
672 : }
673 :
674 : JS_FRIEND_API(bool)
675 0 : js::CheckGrayMarkingState(JSRuntime* rt)
676 : {
677 0 : MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
678 0 : MOZ_ASSERT(!rt->gc.isIncrementalGCInProgress());
679 0 : if (!rt->gc.areGrayBitsValid())
680 0 : return true;
681 :
682 0 : gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
683 0 : AutoTraceSession session(rt, JS::HeapState::Tracing);
684 0 : CheckGrayMarkingTracer tracer(rt);
685 0 : if (!tracer.init())
686 0 : return true; // Ignore failure
687 :
688 0 : return tracer.check(session.lock);
689 : }
690 :
691 : #endif // DEBUG
|