Bug 1189490 - Part 2: Stop using mozilla::LinkedList for the allocations and tenure promotions logs and use js::TraceableFifo instead; r=terrence

This commit is contained in:
Nick Fitzgerald 2015-08-08 15:19:52 -07:00
parent c2227f165b
commit c89311aa13
3 changed files with 133 additions and 157 deletions

View File

@ -357,15 +357,15 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg)
: object(dbg), : object(dbg),
uncaughtExceptionHook(nullptr), uncaughtExceptionHook(nullptr),
enabled(true), enabled(true),
allowUnobservedAsmJS(false),
observedGCs(cx), observedGCs(cx),
tenurePromotionsLog(cx),
trackingTenurePromotions(false), trackingTenurePromotions(false),
tenurePromotionsLogLength(0),
maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH), maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH),
tenurePromotionsLogOverflowed(false), tenurePromotionsLogOverflowed(false),
allowUnobservedAsmJS(false), allocationsLog(cx),
trackingAllocationSites(false), trackingAllocationSites(false),
allocationSamplingProbability(1.0), allocationSamplingProbability(1.0),
allocationsLogLength(0),
maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH), maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
allocationsLogOverflowed(false), allocationsLogOverflowed(false),
frames(cx->runtime()), frames(cx->runtime()),
@ -390,8 +390,8 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg)
Debugger::~Debugger() Debugger::~Debugger()
{ {
MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty()); MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
emptyAllocationsLog(); allocationsLog.clear();
emptyTenurePromotionsLog(); tenurePromotionsLog.clear();
/* /*
* Since the inactive state for this link is a singleton cycle, it's always * Since the inactive state for this link is a singleton cycle, it's always
@ -1701,7 +1701,7 @@ Debugger::isDebuggee(const JSCompartment* compartment) const
return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal()); return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal());
} }
Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when) Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when)
: className(obj.getClass()->name), : className(obj.getClass()->name),
when(when), when(when),
frame(getObjectAllocationSite(obj)), frame(getObjectAllocationSite(obj)),
@ -1712,43 +1712,17 @@ Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject&
void void
Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when) Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when)
{ {
auto* entry = js_new<TenurePromotionsEntry>(rt, obj, when); if (!tenurePromotionsLog.emplaceBack(rt, obj, when))
if (!entry)
CrashAtUnhandlableOOM("Debugger::logTenurePromotion"); CrashAtUnhandlableOOM("Debugger::logTenurePromotion");
tenurePromotionsLog.insertBack(entry); if (tenurePromotionsLog.length() > maxTenurePromotionsLogLength) {
if (tenurePromotionsLogLength >= maxTenurePromotionsLogLength) { if (!tenurePromotionsLog.popFront())
js_delete(tenurePromotionsLog.popFirst()); CrashAtUnhandlableOOM("Debugger::logTenurePromotion");
MOZ_ASSERT(tenurePromotionsLog.length() == maxTenurePromotionsLogLength);
tenurePromotionsLogOverflowed = true; tenurePromotionsLogOverflowed = true;
} else {
tenurePromotionsLogLength++;
} }
} }
/* static */ Debugger::AllocationSite*
Debugger::AllocationSite::create(JSContext* cx, HandleObject frame, double when, HandleObject obj)
{
assertSameCompartment(cx, frame);
RootedAtom ctorName(cx);
{
AutoCompartment ac(cx, obj);
if (!obj->constructorDisplayAtom(cx, &ctorName))
return nullptr;
}
AllocationSite* allocSite = cx->new_<AllocationSite>(frame, when);
if (!allocSite)
return nullptr;
allocSite->className = obj->getClass()->name;
allocSite->ctorName = ctorName.get();
allocSite->size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
return allocSite;
}
bool bool
Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
double when) double when)
@ -1760,38 +1734,34 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame
if (!cx->compartment()->wrap(cx, &wrappedFrame)) if (!cx->compartment()->wrap(cx, &wrappedFrame))
return false; return false;
AllocationSite* allocSite = AllocationSite::create(cx, wrappedFrame, when, obj); RootedAtom ctorName(cx);
if (!allocSite) {
AutoCompartment ac(cx, obj);
if (!obj->constructorDisplayAtom(cx, &ctorName))
return false;
}
auto className = obj->getClass()->name;
auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size))
{
ReportOutOfMemory(cx);
return false; return false;
}
allocationsLog.insertBack(allocSite); if (allocationsLog.length() > maxAllocationsLogLength) {
if (!allocationsLog.popFront()) {
if (allocationsLogLength >= maxAllocationsLogLength) { ReportOutOfMemory(cx);
js_delete(allocationsLog.popFirst()); return false;
}
MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength);
allocationsLogOverflowed = true; allocationsLogOverflowed = true;
} else {
allocationsLogLength++;
} }
return true; return true;
} }
void
Debugger::emptyAllocationsLog()
{
while (!allocationsLog.isEmpty())
js_delete(allocationsLog.popFirst());
allocationsLogLength = 0;
}
void
Debugger::emptyTenurePromotionsLog()
{
while (!tenurePromotionsLog.isEmpty())
js_delete(tenurePromotionsLog.popFirst());
tenurePromotionsLogLength = 0;
}
JSTrapStatus JSTrapStatus
Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp) Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp)
{ {
@ -2332,7 +2302,7 @@ Debugger::removeAllocationsTrackingForAllDebuggees()
for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) { for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
Debugger::removeAllocationsTracking(*r.front().get()); Debugger::removeAllocationsTracking(*r.front().get());
} }
emptyAllocationsLog(); allocationsLog.clear();
} }
@ -2351,19 +2321,7 @@ Debugger::markCrossCompartmentEdges(JSTracer* trc)
// `Debugger::logTenurePromotion`, we can't hold onto CCWs inside the log, // `Debugger::logTenurePromotion`, we can't hold onto CCWs inside the log,
// and instead have unwrapped cross-compartment edges. We need to be sure to // and instead have unwrapped cross-compartment edges. We need to be sure to
// mark those here. // mark those here.
traceTenurePromotionsLog(trc); TenurePromotionsLog::trace(&tenurePromotionsLog, trc);
}
/*
* Trace every entry in the promoted to tenured heap log.
*/
void
Debugger::traceTenurePromotionsLog(JSTracer* trc)
{
for (TenurePromotionsEntry* e = tenurePromotionsLog.getFirst(); e; e = e->getNext()) {
if (e->frame)
TraceEdge(trc, &e->frame, "Debugger::tenurePromotionsLog SavedFrame");
}
} }
/* /*
@ -2544,17 +2502,8 @@ Debugger::trace(JSTracer* trc)
TraceEdge(trc, &frameobj, "live Debugger.Frame"); TraceEdge(trc, &frameobj, "live Debugger.Frame");
} }
/* AllocationsLog::trace(&allocationsLog, trc);
* Mark every allocation site in our allocation log. TenurePromotionsLog::trace(&tenurePromotionsLog, trc);
*/
for (AllocationSite* s = allocationsLog.getFirst(); s; s = s->getNext()) {
if (s->frame)
TraceEdge(trc, &s->frame, "allocation log SavedFrame");
if (s->ctorName)
TraceEdge(trc, &s->ctorName, "allocation log constructor name");
}
traceTenurePromotionsLog(trc);
/* Trace the weak map from JSScript instances to Debugger.Script objects. */ /* Trace the weak map from JSScript instances to Debugger.Script objects. */
scripts.trace(trc); scripts.trace(trc);

View File

@ -24,6 +24,7 @@
#include "js/HashTable.h" #include "js/HashTable.h"
#include "vm/GlobalObject.h" #include "vm/GlobalObject.h"
#include "vm/SavedStacks.h" #include "vm/SavedStacks.h"
#include "js/TraceableFifo.h"
enum JSTrapStatus { enum JSTrapStatus {
JSTRAP_ERROR, JSTRAP_ERROR,
@ -283,63 +284,72 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when); void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when);
static JSObject* getObjectAllocationSite(JSObject& obj); static JSObject* getObjectAllocationSite(JSObject& obj);
private: struct TenurePromotionsLogEntry : public JS::Traceable
HeapPtrNativeObject object; /* The Debugger object. Strong reference. */
WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
bool enabled;
JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */
// The set of GC numbers for which one or more of this Debugger's observed
// debuggees participated in.
js::HashSet<uint64_t> observedGCs;
struct TenurePromotionsEntry : public mozilla::LinkedListElement<TenurePromotionsEntry>
{ {
TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when); TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when);
const char* className; const char* className;
double when; double when;
RelocatablePtrObject frame; RelocatablePtrObject frame;
size_t size; size_t size;
static void trace(TenurePromotionsLogEntry* e, JSTracer* trc) {
if (e->frame)
TraceEdge(trc, &e->frame, "Debugger::TenurePromotionsLogEntry::frame");
}
}; };
using TenurePromotionsLog = mozilla::LinkedList<TenurePromotionsEntry>; struct AllocationsLogEntry : public JS::Traceable
TenurePromotionsLog tenurePromotionsLog;
bool trackingTenurePromotions;
size_t tenurePromotionsLogLength;
size_t maxTenurePromotionsLogLength;
bool tenurePromotionsLogOverflowed;
struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
{ {
AllocationSite(HandleObject frame, double when) AllocationsLogEntry(HandleObject frame, double when, const char* className,
HandleAtom ctorName, size_t size)
: frame(frame), : frame(frame),
when(when), when(when),
className(nullptr), className(className),
ctorName(nullptr), ctorName(ctorName),
size(0) size(size)
{ {
MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>()); MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
}; };
static AllocationSite* create(JSContext* cx, HandleObject frame, double when,
HandleObject obj);
RelocatablePtrObject frame; RelocatablePtrObject frame;
double when; double when;
const char* className; const char* className;
RelocatablePtrAtom ctorName; RelocatablePtrAtom ctorName;
size_t size; size_t size;
};
typedef mozilla::LinkedList<AllocationSite> AllocationSiteList;
static void trace(AllocationsLogEntry* e, JSTracer* trc) {
if (e->frame)
TraceEdge(trc, &e->frame, "Debugger::AllocationsLogEntry::frame");
if (e->ctorName)
TraceEdge(trc, &e->ctorName, "Debugger::AllocationsLogEntry::ctorName");
}
};
private:
HeapPtrNativeObject object; /* The Debugger object. Strong reference. */
WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
bool enabled;
bool allowUnobservedAsmJS; bool allowUnobservedAsmJS;
JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */
// The set of GC numbers for which one or more of this Debugger's observed
// debuggees participated in.
js::HashSet<uint64_t> observedGCs;
using TenurePromotionsLog = js::TraceableFifo<TenurePromotionsLogEntry>;
TenurePromotionsLog tenurePromotionsLog;
bool trackingTenurePromotions;
size_t maxTenurePromotionsLogLength;
bool tenurePromotionsLogOverflowed;
using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
AllocationsLog allocationsLog;
bool trackingAllocationSites; bool trackingAllocationSites;
double allocationSamplingProbability; double allocationSamplingProbability;
AllocationSiteList allocationsLog;
size_t allocationsLogLength;
size_t maxAllocationsLogLength; size_t maxAllocationsLogLength;
bool allocationsLogOverflowed; bool allocationsLogOverflowed;
@ -347,8 +357,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
double when); double when);
void emptyAllocationsLog();
void emptyTenurePromotionsLog();
/* /*
* Recompute the set of debuggee zones based on the set of debuggee globals. * Recompute the set of debuggee zones based on the set of debuggee globals.
@ -503,7 +511,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
void trace(JSTracer* trc); void trace(JSTracer* trc);
static void finalize(FreeOp* fop, JSObject* obj); static void finalize(FreeOp* fop, JSObject* obj);
void markCrossCompartmentEdges(JSTracer* tracer); void markCrossCompartmentEdges(JSTracer* tracer);
void traceTenurePromotionsLog(JSTracer* trc);
static const Class jsclass; static const Class jsclass;
@ -920,6 +927,20 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
Debugger & operator=(const Debugger&) = delete; Debugger & operator=(const Debugger&) = delete;
}; };
template<>
struct DefaultTracer<Debugger::TenurePromotionsLogEntry> {
static void trace(JSTracer* trc, Debugger::TenurePromotionsLogEntry* e, const char*) {
Debugger::TenurePromotionsLogEntry::trace(e, trc);
}
};
template<>
struct DefaultTracer<Debugger::AllocationsLogEntry> {
static void trace(JSTracer* trc, Debugger::AllocationsLogEntry* e, const char*) {
Debugger::AllocationsLogEntry::trace(e, trc);
}
};
class BreakpointSite { class BreakpointSite {
friend class Breakpoint; friend class Breakpoint;
friend struct ::JSCompartment; friend struct ::JSCompartment;

View File

@ -185,7 +185,7 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
return false; return false;
} }
size_t length = dbg->allocationsLogLength; size_t length = dbg->allocationsLog.length();
RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length)); RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
if (!result) if (!result)
@ -197,21 +197,21 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
if (!obj) if (!obj)
return false; return false;
// Don't pop the AllocationSite yet. The queue's links are followed by // Don't pop the AllocationsLogEntry yet. The queue's links are followed
// the GC to find the AllocationSite, but are not barriered, so we must // by the GC to find the AllocationsLogEntry, but are not barriered, so
// edit them with great care. Use the queue entry in place, and then // we must edit them with great care. Use the queue entry in place, and
// pop and delete together. // then pop and delete together.
Debugger::AllocationSite* allocSite = dbg->allocationsLog.getFirst(); Debugger::AllocationsLogEntry& entry = dbg->allocationsLog.front();
RootedValue frame(cx, ObjectOrNullValue(allocSite->frame)); RootedValue frame(cx, ObjectOrNullValue(entry.frame));
if (!DefineProperty(cx, obj, cx->names().frame, frame)) if (!DefineProperty(cx, obj, cx->names().frame, frame))
return false; return false;
RootedValue timestampValue(cx, NumberValue(allocSite->when)); RootedValue timestampValue(cx, NumberValue(entry.when));
if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
return false; return false;
RootedString className(cx, Atomize(cx, allocSite->className, strlen(allocSite->className))); RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className)));
if (!className) if (!className)
return false; return false;
RootedValue classNameValue(cx, StringValue(className)); RootedValue classNameValue(cx, StringValue(className));
@ -219,26 +219,27 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp)
return false; return false;
RootedValue ctorName(cx, NullValue()); RootedValue ctorName(cx, NullValue());
if (allocSite->ctorName) if (entry.ctorName)
ctorName.setString(allocSite->ctorName); ctorName.setString(entry.ctorName);
if (!DefineProperty(cx, obj, cx->names().constructor, ctorName)) if (!DefineProperty(cx, obj, cx->names().constructor, ctorName))
return false; return false;
RootedValue size(cx, NumberValue(allocSite->size)); RootedValue size(cx, NumberValue(entry.size));
if (!DefineProperty(cx, obj, cx->names().size, size)) if (!DefineProperty(cx, obj, cx->names().size, size))
return false; return false;
result->setDenseElement(i, ObjectValue(*obj)); result->setDenseElement(i, ObjectValue(*obj));
// Pop the front queue entry, and delete it immediately, so that // Pop the front queue entry, and delete it immediately, so that the GC
// the GC sees the AllocationSite's RelocatablePtr barriers run // sees the AllocationsLogEntry's RelocatablePtr barriers run atomically
// atomically with the change to the graph (the queue link). // with the change to the graph (the queeue link).
MOZ_ALWAYS_TRUE(dbg->allocationsLog.popFirst() == allocSite); if (!dbg->allocationsLog.popFront()) {
js_delete(allocSite); ReportOutOfMemory(cx);
return false;
}
} }
dbg->allocationsLogOverflowed = false; dbg->allocationsLogOverflowed = false;
dbg->allocationsLogLength = 0;
args.rval().setObject(*result); args.rval().setObject(*result);
return true; return true;
} }
@ -272,9 +273,11 @@ DebuggerMemory::setMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value*
Debugger* dbg = memory->getDebugger(); Debugger* dbg = memory->getDebugger();
dbg->maxAllocationsLogLength = max; dbg->maxAllocationsLogLength = max;
while (dbg->allocationsLogLength > dbg->maxAllocationsLogLength) { while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) {
js_delete(dbg->allocationsLog.getFirst()); if (!dbg->allocationsLog.popFront()) {
dbg->allocationsLogLength--; ReportOutOfMemory(cx);
return false;
}
} }
args.rval().setUndefined(); args.rval().setUndefined();
@ -352,7 +355,7 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp
return false; return false;
} }
size_t length = dbg->tenurePromotionsLogLength; size_t length = dbg->tenurePromotionsLog.length();
RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length)); RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
if (!result) if (!result)
@ -368,41 +371,42 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp
// followed by the GC to find the TenurePromotionsEntry, but are not // followed by the GC to find the TenurePromotionsEntry, but are not
// barriered, so we must edit them with great care. Use the queue entry // barriered, so we must edit them with great care. Use the queue entry
// in place, and then pop and delete together. // in place, and then pop and delete together.
auto* entry = dbg->tenurePromotionsLog.getFirst(); auto& entry = dbg->tenurePromotionsLog.front();
RootedValue frame(cx, ObjectOrNullValue(entry->frame)); RootedValue frame(cx, ObjectOrNullValue(entry.frame));
if (!cx->compartment()->wrap(cx, &frame) || if (!cx->compartment()->wrap(cx, &frame) ||
!DefineProperty(cx, obj, cx->names().frame, frame)) !DefineProperty(cx, obj, cx->names().frame, frame))
{ {
return false; return false;
} }
RootedValue timestampValue(cx, NumberValue(entry->when)); RootedValue timestampValue(cx, NumberValue(entry.when));
if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
return false; return false;
RootedString className(cx, Atomize(cx, entry->className, strlen(entry->className))); RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className)));
if (!className) if (!className)
return false; return false;
RootedValue classNameValue(cx, StringValue(className)); RootedValue classNameValue(cx, StringValue(className));
if (!DefineProperty(cx, obj, cx->names().class_, classNameValue)) if (!DefineProperty(cx, obj, cx->names().class_, classNameValue))
return false; return false;
RootedValue sizeValue(cx, NumberValue(entry->size)); RootedValue sizeValue(cx, NumberValue(entry.size));
if (!DefineProperty(cx, obj, cx->names().size, sizeValue)) if (!DefineProperty(cx, obj, cx->names().size, sizeValue))
return false; return false;
result->setDenseElement(i, ObjectValue(*obj)); result->setDenseElement(i, ObjectValue(*obj));
// Pop the front queue entry, and delete it immediately, so that // Pop the front queue entry, and delete it immediately, so that the GC
// the GC sees the TenurePromotionsEntry's RelocatablePtr barriers run // sees the TenurePromotionsEntry's RelocatablePtr barriers run
// atomically with the change to the graph (the queue link). // atomically with the change to the graph (the queue link).
MOZ_ALWAYS_TRUE(dbg->tenurePromotionsLog.popFirst() == entry); if (!dbg->tenurePromotionsLog.popFront()) {
js_delete(entry); ReportOutOfMemory(cx);
return false;
}
} }
dbg->tenurePromotionsLogOverflowed = false; dbg->tenurePromotionsLogOverflowed = false;
dbg->tenurePromotionsLogLength = 0;
args.rval().setObject(*result); args.rval().setObject(*result);
return true; return true;
} }
@ -436,9 +440,11 @@ DebuggerMemory::setMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Va
Debugger* dbg = memory->getDebugger(); Debugger* dbg = memory->getDebugger();
dbg->maxTenurePromotionsLogLength = max; dbg->maxTenurePromotionsLogLength = max;
while (dbg->tenurePromotionsLogLength > dbg->maxAllocationsLogLength) { while (dbg->tenurePromotionsLog.length() > dbg->maxAllocationsLogLength) {
js_delete(dbg->tenurePromotionsLog.getFirst()); if (!dbg->tenurePromotionsLog.popFront()) {
dbg->tenurePromotionsLogLength--; ReportOutOfMemory(cx);
return false;
}
} }
args.rval().setUndefined(); args.rval().setUndefined();