Mypal/js/src/gc/Verifier.cpp

155 lines
3.7 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef MOZ_VALGRIND
# include <valgrind/memcheck.h>
#endif
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Sprintf.h"
#include "jscntxt.h"
#include "jsgc.h"
#include "jsprf.h"
#include "gc/GCInternals.h"
#include "gc/Zone.h"
#include "js/GCAPI.h"
#include "js/HashTable.h"
#include "jscntxtinlines.h"
#include "jsgcinlines.h"
using namespace js;
using namespace js::gc;
#ifdef JSGC_HASH_TABLE_CHECKS
class CheckHeapTracer : public JS::CallbackTracer
{
public:
explicit CheckHeapTracer(JSRuntime* rt);
bool init();
void check(AutoLockForExclusiveAccess& lock);
private:
void onChild(const JS::GCCellPtr& thing) override;
struct WorkItem {
WorkItem(JS::GCCellPtr thing, const char* name, int parentIndex)
: thing(thing), name(name), parentIndex(parentIndex), processed(false)
{}
JS::GCCellPtr thing;
const char* name;
int parentIndex;
bool processed;
};
JSRuntime* rt;
bool oom;
size_t failures;
HashSet<Cell*, DefaultHasher<Cell*>, SystemAllocPolicy> visited;
Vector<WorkItem, 0, SystemAllocPolicy> stack;
int parentIndex;
};
CheckHeapTracer::CheckHeapTracer(JSRuntime* rt)
: CallbackTracer(rt, TraceWeakMapKeysValues),
rt(rt),
oom(false),
failures(0),
parentIndex(-1)
{
#ifdef DEBUG
setCheckEdges(false);
#endif
}
bool
CheckHeapTracer::init()
{
return visited.init();
}
inline static bool
IsValidGCThingPointer(Cell* cell)
{
return (uintptr_t(cell) & CellMask) == 0;
}
void
CheckHeapTracer::onChild(const JS::GCCellPtr& thing)
{
Cell* cell = thing.asCell();
if (visited.lookup(cell))
return;
if (!visited.put(cell)) {
oom = true;
return;
}
if (!IsValidGCThingPointer(cell) || !IsGCThingValidAfterMovingGC(cell))
{
failures++;
fprintf(stderr, "Bad pointer %p\n", cell);
const char* name = contextName();
for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
const WorkItem& parent = stack[index];
cell = parent.thing.asCell();
fprintf(stderr, " from %s %p %s edge\n",
GCTraceKindToAscii(cell->getTraceKind()), cell, name);
name = parent.name;
}
fprintf(stderr, " from root %s\n", name);
return;
}
WorkItem item(thing, contextName(), parentIndex);
if (!stack.append(item))
oom = true;
}
void
CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
{
// The analysis thinks that traceRuntime might GC by calling a GC callback.
JS::AutoSuppressGCAnalysis nogc;
if (!rt->isBeingDestroyed())
rt->gc.traceRuntime(this, lock);
while (!stack.empty()) {
WorkItem item = stack.back();
if (item.processed) {
stack.popBack();
} else {
parentIndex = stack.length() - 1;
TraceChildren(this, item.thing);
stack.back().processed = true;
}
}
if (oom)
return;
if (failures) {
fprintf(stderr, "Heap check: %" PRIuSIZE " failure(s) out of %" PRIu32 " pointers checked\n",
failures, visited.count());
}
MOZ_RELEASE_ASSERT(failures == 0);
}
void
js::gc::CheckHeapAfterGC(JSRuntime* rt)
{
AutoTraceSession session(rt, JS::HeapState::Tracing);
CheckHeapTracer tracer(rt);
if (tracer.init())
tracer.check(session.lock);
}
#endif /* JSGC_HASH_TABLE_CHECKS */