Bug 1051115: Let users tell ubi::Edge not to provide edge names, as they're expensive to compute, and not everyone needs them. r=terrence

This commit is contained in:
Jim Blandy 2014-08-14 15:59:59 -07:00
parent 09c17f52c5
commit b155d53d07
4 changed files with 52 additions and 32 deletions

View File

@ -198,7 +198,10 @@ class Base {
// edges. The EdgeRange should be freed with 'js_delete'. (You could use
// ScopedDJSeletePtr<EdgeRange> to manage it.) On OOM, report an exception
// on |cx| and return nullptr.
virtual EdgeRange *edges(JSContext *cx) const = 0;
//
// If wantNames is true, compute names for edges. Doing so can be expensive
// in time and memory.
virtual EdgeRange *edges(JSContext *cx, bool wantNames) const = 0;
// Return the Zone to which this node's referent belongs, or nullptr if the
// referent is not of a type allocated in SpiderMonkey Zones.
@ -333,9 +336,11 @@ class Node {
const jschar *typeName() const { return base()->typeName(); }
size_t size() const { return base()->size(); }
EdgeRange *edges(JSContext *cx) const { return base()->edges(cx); }
JS::Zone *zone() const { return base()->zone(); }
JSCompartment *compartment() const { return base()->compartment(); }
EdgeRange *edges(JSContext *cx, bool wantNames = true) const {
return base()->edges(cx, wantNames);
}
// A hash policy for ubi::Nodes.
// This simply uses the stock PointerHasher on the ubi::Node's pointer.
@ -365,7 +370,8 @@ class Edge {
virtual ~Edge() { }
public:
// This edge's name.
// This edge's name. This may be nullptr, if Node::edges was called with
// false as the wantNames parameter.
//
// The storage is owned by this Edge, and will be freed when this Edge is
// destructed.
@ -428,7 +434,7 @@ template<typename Referent>
class TracerConcrete : public Base {
const jschar *typeName() const MOZ_OVERRIDE { return concreteTypeName; }
size_t size() const MOZ_OVERRIDE { return 0; } // not implemented yet; bug 1011300
EdgeRange *edges(JSContext *) const MOZ_OVERRIDE;
EdgeRange *edges(JSContext *, bool wantNames) const MOZ_OVERRIDE;
JS::Zone *zone() const MOZ_OVERRIDE { return get().zone(); }
JSCompartment *compartment() const MOZ_OVERRIDE { return nullptr; }
@ -472,7 +478,7 @@ template<>
class Concrete<void> : public Base {
const jschar *typeName() const MOZ_OVERRIDE;
size_t size() const MOZ_OVERRIDE;
EdgeRange *edges(JSContext *cx) const MOZ_OVERRIDE;
EdgeRange *edges(JSContext *cx, bool wantNames) const MOZ_OVERRIDE;
JS::Zone *zone() const MOZ_OVERRIDE;
JSCompartment *compartment() const MOZ_OVERRIDE;

View File

@ -84,7 +84,7 @@ struct BreadthFirst {
// We do nothing with noGC, other than require it to exist, with a lifetime
// that encloses our own.
BreadthFirst(JSContext *cx, Handler &handler, const JS::AutoCheckCannotGC &noGC)
: cx(cx), visited(cx), handler(handler), pending(cx),
: wantNames(true), cx(cx), visited(cx), handler(handler), pending(cx),
traversalBegun(false), stopRequested(false), abandonRequested(false)
{ }
@ -95,6 +95,10 @@ struct BreadthFirst {
// as many starting points as you like. Return false on OOM.
bool addStart(Node node) { return pending.append(node); }
// True if the handler wants us to compute edge names; doing so can be
// expensive in time and memory. True by default.
bool wantNames;
// Traverse the graph in breadth-first order, starting at the given
// start nodes, applying |handler::operator()| for each edge traversed
// as described above.
@ -113,7 +117,7 @@ struct BreadthFirst {
pending.popFront();
// Get a range containing all origin's outgoing edges.
js::ScopedJSDeletePtr<EdgeRange> range(origin.edges(cx));
js::ScopedJSDeletePtr<EdgeRange> range(origin.edges(cx, wantNames));
if (!range)
return false;

View File

@ -643,6 +643,7 @@ DebuggerMemory::takeCensus(JSContext *cx, unsigned argc, Value *vp)
dbg::DefaultCensusTraversal traversal(cx, handler, noGC);
if (!traversal.init())
return false;
traversal.wantNames = false;
// Walk the debuggee compartments, using it to set the starting points
// (the debuggee globals) for the traversal, and to populate

View File

@ -28,11 +28,11 @@ using JS::ubi::Node;
using JS::ubi::TracerConcrete;
// All operations on null ubi::Nodes crash.
const jschar *Concrete<void>::typeName() const { MOZ_CRASH("null ubi::Node"); }
size_t Concrete<void>::size() const { MOZ_CRASH("null ubi::Node"); }
EdgeRange *Concrete<void>::edges(JSContext *) const { MOZ_CRASH("null ubi::Node"); }
JS::Zone *Concrete<void>::zone() const { MOZ_CRASH("null ubi::Node"); }
JSCompartment *Concrete<void>::compartment() const { MOZ_CRASH("null ubi::Node"); }
const jschar *Concrete<void>::typeName() const { MOZ_CRASH("null ubi::Node"); }
size_t Concrete<void>::size() const { MOZ_CRASH("null ubi::Node"); }
EdgeRange *Concrete<void>::edges(JSContext *, bool) const { MOZ_CRASH("null ubi::Node"); }
JS::Zone *Concrete<void>::zone() const { MOZ_CRASH("null ubi::Node"); }
JSCompartment *Concrete<void>::compartment() const { MOZ_CRASH("null ubi::Node"); }
Node::Node(JSGCTraceKind kind, void *ptr)
{
@ -132,6 +132,9 @@ class SimpleEdgeVectorTracer : public JSTracer {
// The vector to which we add SimpleEdges.
SimpleEdgeVector *vec;
// True if we should populate the edge's names.
bool wantNames;
static void staticCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind) {
static_cast<SimpleEdgeVectorTracer *>(trc)->callback(thingp, kind);
}
@ -140,22 +143,25 @@ class SimpleEdgeVectorTracer : public JSTracer {
if (!okay)
return;
// Ask the tracer to compute an edge name for us.
char buffer[1024];
const char *name = getTracingEdgeName(buffer, sizeof(buffer));
jschar *jsname = nullptr;
if (wantNames) {
// Ask the tracer to compute an edge name for us.
char buffer[1024];
const char *name = getTracingEdgeName(buffer, sizeof(buffer));
// Convert the name to jschars.
jschar *jsname = js_pod_malloc<jschar>(strlen(name) + 1);
if (!jsname) {
okay = false;
return;
// Convert the name to jschars.
jsname = js_pod_malloc<jschar>(strlen(name) + 1);
if (!jsname) {
okay = false;
return;
}
size_t i;
for (i = 0; name[i]; i++)
jsname[i] = name[i];
jsname[i] = '\0';
}
size_t i;
for (i = 0; name[i]; i++)
jsname[i] = name[i];
jsname[i] = '\0';
// The simplest code is correct! The temporary SimpleEdge takes
// ownership of name; if the append succeeds, the vector element
// then takes ownership; if the append fails, then the temporary
@ -170,9 +176,12 @@ class SimpleEdgeVectorTracer : public JSTracer {
// True if no errors (OOM, say) have yet occurred.
bool okay;
SimpleEdgeVectorTracer(JSContext *cx, SimpleEdgeVector *vec)
: JSTracer(JS_GetRuntime(cx), staticCallback), vec(vec), okay(true) {
}
SimpleEdgeVectorTracer(JSContext *cx, SimpleEdgeVector *vec, bool wantNames)
: JSTracer(JS_GetRuntime(cx), staticCallback),
vec(vec),
wantNames(wantNames),
okay(true)
{ }
};
@ -189,8 +198,8 @@ class SimpleEdgeRange : public EdgeRange {
public:
explicit SimpleEdgeRange(JSContext *cx) : edges(cx), i(0) { }
bool init(JSContext *cx, void *thing, JSGCTraceKind kind) {
SimpleEdgeVectorTracer tracer(cx, &edges);
bool init(JSContext *cx, void *thing, JSGCTraceKind kind, bool wantNames = true) {
SimpleEdgeVectorTracer tracer(cx, &edges, wantNames);
JS_TraceChildren(&tracer, thing, kind);
settle();
return tracer.okay;
@ -202,12 +211,12 @@ class SimpleEdgeRange : public EdgeRange {
template<typename Referent>
EdgeRange *
TracerConcrete<Referent>::edges(JSContext *cx) const {
TracerConcrete<Referent>::edges(JSContext *cx, bool wantNames) const {
js::ScopedJSDeletePtr<SimpleEdgeRange> r(js_new<SimpleEdgeRange>(cx));
if (!r)
return nullptr;
if (!r->init(cx, ptr, ::js::gc::MapTypeToTraceKind<Referent>::kind))
if (!r->init(cx, ptr, ::js::gc::MapTypeToTraceKind<Referent>::kind, wantNames))
return nullptr;
return r.forget();