зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1169710 - Part 2: Add Debugger infrastructure for logging promotions to the tenured heap; r=sfink
This commit is contained in:
Родитель
67d68f4c7f
Коммит
ca32794c23
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче