Bug 1169710 - Part 2: Add Debugger infrastructure for logging promotions to the tenured heap; r=sfink

This commit is contained in:
Nick Fitzgerald 2015-07-10 19:14:08 -07:00
Родитель 67d68f4c7f
Коммит ca32794c23
6 изменённых файлов: 139 добавлений и 17 удалений

Просмотреть файл

@ -1894,6 +1894,7 @@ js::TenuringTracer::moveToTenured(JSObject* src)
AllocKind dstKind = src->allocKindForTenure(nursery());
Zone* zone = src->zone();
TenuredCell* t = zone->arenas.allocateFromFreeList(dstKind, Arena::thingSize(dstKind));
if (!t) {
zone->arenas.checkEmptyFreeList(dstKind);
@ -1910,6 +1911,10 @@ js::TenuringTracer::moveToTenured(JSObject* src)
overlay->forwardTo(dst);
insertIntoFixupList(overlay);
if (MOZ_UNLIKELY(zone->hasDebuggers())) {
zone->enqueueForPromotionToTenuredLogging(*dst);
}
TracePromoteToTenured(src, dst);
return dst;
}

Просмотреть файл

@ -496,6 +496,12 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
sweep();
TIME_END(sweep);
TIME_START(logPromotionsToTenured);
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
zone->logPromotionsToTenured();
}
TIME_END(logPromotionsToTenured);
TIME_START(clearStoreBuffer);
rt->gc.storeBuffer.clear();
TIME_END(clearStoreBuffer);
@ -549,13 +555,13 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
static bool printedHeader = false;
if (!printedHeader) {
fprintf(stderr,
"MinorGC: Reason PRate Size Time mkVals mkClls mkSlts mkWCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB sweep resize pretnr\n");
"MinorGC: Reason PRate Size Time mkVals mkClls mkSlts mkWCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB sweep logPtT resize pretnr\n");
printedHeader = true;
}
#define FMT " %6" PRIu64
fprintf(stderr,
"MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
"MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
js::gcstats::ExplainReason(reason),
promotionRate * 100,
numActiveChunks_,
@ -576,6 +582,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList
TIME_TOTAL(freeMallocedBuffers),
TIME_TOTAL(clearStoreBuffer),
TIME_TOTAL(sweep),
TIME_TOTAL(logPromotionsToTenured),
TIME_TOTAL(resize),
TIME_TOTAL(pretenure));
#undef FMT

Просмотреть файл

@ -132,6 +132,29 @@ Zone::getOrCreateDebuggers(JSContext* cx)
return debuggers;
}
void
Zone::logPromotionsToTenured()
{
auto* dbgs = getDebuggers();
if (MOZ_LIKELY(!dbgs))
return;
auto now = JS_GetCurrentEmbedderTime();
for (auto** dbgp = dbgs->begin(); dbgp != dbgs->end(); dbgp++) {
if (!(*dbgp)->isEnabled() || !(*dbgp)->isTrackingTenurePromotions())
continue;
for (auto range = awaitingTenureLogging.all(); !range.empty(); range.popFront()) {
if ((*dbgp)->isDebuggee(range.front()->compartment()))
(*dbgp)->logTenurePromotion(*range.front(), now);
}
}
awaitingTenureLogging.clear();
}
void
Zone::sweepBreakpoints(FreeOp* fop)
{

Просмотреть файл

@ -236,6 +236,9 @@ struct Zone : public JS::shadow::Zone,
private:
DebuggerVector* debuggers;
using LogTenurePromotionQueue = js::Vector<JSObject*, 0, js::SystemAllocPolicy>;
LogTenurePromotionQueue awaitingTenureLogging;
void sweepBreakpoints(js::FreeOp* fop);
void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC);
@ -250,6 +253,14 @@ struct Zone : public JS::shadow::Zone,
DebuggerVector* getDebuggers() const { return debuggers; }
DebuggerVector* getOrCreateDebuggers(JSContext* cx);
void enqueueForPromotionToTenuredLogging(JSObject& obj) {
MOZ_ASSERT(hasDebuggers());
MOZ_ASSERT(!IsInsideNursery(&obj));
if (!awaitingTenureLogging.append(&obj))
js::CrashAtUnhandlableOOM("Zone::enqueueForPromotionToTenuredLogging");
}
void logPromotionsToTenured();
js::gc::ArenaLists arenas;
js::TypeZone types;

Просмотреть файл

@ -363,11 +363,15 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg)
uncaughtExceptionHook(nullptr),
enabled(true),
observedGCs(cx),
trackingTenurePromotions(false),
tenurePromotionsLogLength(0),
maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH),
tenurePromotionsLogOverflowed(false),
allowUnobservedAsmJS(false),
trackingAllocationSites(false),
allocationSamplingProbability(1.0),
allocationsLogLength(0),
maxAllocationsLogLength(DEFAULT_MAX_ALLOCATIONS_LOG_LENGTH),
maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
allocationsLogOverflowed(false),
frames(cx->runtime()),
scripts(cx),
@ -392,6 +396,7 @@ Debugger::~Debugger()
{
MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
emptyAllocationsLog();
emptyTenurePromotionsLog();
/*
* Since the inactive state for this link is a singleton cycle, it's always
@ -407,6 +412,7 @@ bool
Debugger::init(JSContext* cx)
{
bool ok = debuggees.init() &&
debuggeeZones.init() &&
frames.init() &&
scripts.init() &&
sources.init() &&
@ -1700,6 +1706,22 @@ Debugger::isDebuggee(const JSCompartment* compartment) const
return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal());
}
void
Debugger::logTenurePromotion(JSObject& obj, double when)
{
auto* entry = js_new<TenurePromotionsEntry>(obj, when);
if (!entry)
CrashAtUnhandlableOOM("Debugger::logTenurePromotion");
tenurePromotionsLog.insertBack(entry);
if (tenurePromotionsLogLength >= maxTenurePromotionsLogLength) {
js_delete(tenurePromotionsLog.popFirst());
tenurePromotionsLogOverflowed = true;
} else {
tenurePromotionsLogLength++;
}
}
/* static */ Debugger::AllocationSite*
Debugger::AllocationSite::create(JSContext* cx, HandleObject frame, double when, HandleObject obj)
{
@ -1742,7 +1764,7 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame
allocationsLog.insertBack(allocSite);
if (allocationsLogLength >= maxAllocationsLogLength) {
js_delete(allocationsLog.getFirst());
js_delete(allocationsLog.popFirst());
allocationsLogOverflowed = true;
} else {
allocationsLogLength++;
@ -1755,10 +1777,18 @@ void
Debugger::emptyAllocationsLog()
{
while (!allocationsLog.isEmpty())
js_delete(allocationsLog.getFirst());
js_delete(allocationsLog.popFirst());
allocationsLogLength = 0;
}
void
Debugger::emptyTenurePromotionsLog()
{
while (!tenurePromotionsLog.isEmpty())
js_delete(tenurePromotionsLog.popFirst());
tenurePromotionsLogLength = 0;
}
JSTrapStatus
Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp)
{
@ -2504,6 +2534,14 @@ Debugger::trace(JSTracer* trc)
TraceEdge(trc, &s->ctorName, "allocation log constructor name");
}
/*
* Mark every entry in the promoted to tenured heap log.
*/
for (TenurePromotionsEntry* e = tenurePromotionsLog.getFirst(); e; e = e->getNext()) {
if (e->frame)
TraceEdge(trc, &e->frame, "tenure promotions log SavedFrame");
}
/* Trace the weak map from JSScript instances to Debugger.Script objects. */
scripts.trace(trc);
@ -6950,24 +6988,30 @@ null(CallArgs& args)
return true;
}
/* static */ JSObject*
Debugger::getObjectAllocationSite(JSObject& obj)
{
JSObject* metadata = GetObjectMetadata(&obj);
if (!metadata)
return nullptr;
MOZ_ASSERT(!metadata->is<WrapperObject>());
if (!SavedFrame::isSavedFrameAndNotProto(*metadata))
return nullptr;
return metadata;
}
static bool
DebuggerObject_getAllocationSite(JSContext* cx, unsigned argc, Value* vp)
{
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get allocationSite", args, obj);
RootedObject metadata(cx, GetObjectMetadata(obj));
if (!metadata)
RootedObject allocSite(cx, Debugger::getObjectAllocationSite(*obj));
if (!allocSite)
return null(args);
MOZ_ASSERT(!metadata->is<WrapperObject>());
if (!SavedFrame::isSavedFrameAndNotProto(*metadata))
return null(args);
if (!cx->compartment()->wrap(cx, &metadata))
if (!cx->compartment()->wrap(cx, &allocSite))
return false;
args.rval().setObject(*metadata);
args.rval().setObject(*allocSite);
return true;
}

Просмотреть файл

@ -261,6 +261,17 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
return observedGCs.put(majorGCNumber);
}
bool isTrackingTenurePromotions() const {
return trackingTenurePromotions;
}
bool isEnabled() const {
return enabled;
}
void logTenurePromotion(JSObject& obj, double when);
static JSObject* getObjectAllocationSite(JSObject& obj);
private:
HeapPtrNativeObject object; /* The Debugger object. Strong reference. */
WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
@ -273,6 +284,26 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
// debuggees participated in.
js::HashSet<uint64_t> observedGCs;
struct TenurePromotionsEntry : public mozilla::LinkedListElement<TenurePromotionsEntry>
{
TenurePromotionsEntry(JSObject& obj, double when)
: className(obj.getClass()->name),
when(when),
frame(getObjectAllocationSite(obj))
{ }
const char* className;
double when;
RelocatablePtrObject frame;
};
using TenurePromotionsLog = mozilla::LinkedList<TenurePromotionsEntry>;
TenurePromotionsLog tenurePromotionsLog;
bool trackingTenurePromotions;
size_t tenurePromotionsLogLength;
size_t maxTenurePromotionsLogLength;
bool tenurePromotionsLogOverflowed;
struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
{
AllocationSite(HandleObject frame, double when)
@ -304,11 +335,12 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
size_t maxAllocationsLogLength;
bool allocationsLogOverflowed;
static const size_t DEFAULT_MAX_ALLOCATIONS_LOG_LENGTH = 5000;
static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
double when);
void emptyAllocationsLog();
void emptyTenurePromotionsLog();
/*
* Recompute the set of debuggee zones based on the set of debuggee globals.