author | Andrew McCreight <continuation@gmail.com> |
Fri, 14 Mar 2014 16:07:07 -0700 | |
changeset 173719 | 0a1597e03bdeb3cf65916ab77a0c9fb5aba891a4 |
parent 173718 | 6b45e3aa3fda4b435c3705cdf61d11d678feb323 |
child 173720 | b6bf2687d93fd3322dd318b158189956fc524b9c |
push id | 26415 |
push user | kwierso@gmail.com |
push date | Sat, 15 Mar 2014 05:20:40 +0000 |
treeherder | mozilla-central@82c90c17fc95 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 948755 |
milestone | 30.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
xpcom/base/nsCycleCollector.cpp | file | annotate | diff | comparison | revisions | |
xpcom/base/nsICycleCollectorListener.idl | file | annotate | diff | comparison | revisions |
--- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -1569,16 +1569,24 @@ public: { if (!mDisableLog) { fprintf(mStream, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n", (void*)aMap, (void*)aKey, (void*)aKeyDelegate, (void*)aValue); } // We don't support after-processing for weak map entries. return NS_OK; } + NS_IMETHOD NoteIncrementalRoot(uint64_t aAddress) + { + if (!mDisableLog) { + fprintf(mStream, "IncrementalRoot %p\n", (void*)aAddress); + } + // We don't support after-processing for incremental roots. + return NS_OK; + } NS_IMETHOD BeginResults() { if (!mDisableLog) { fputs("==========\n", mStream); } return NS_OK; } NS_IMETHOD DescribeRoot(uint64_t aAddress, uint32_t aKnownEdges) @@ -2702,18 +2710,19 @@ nsCycleCollector::ScanWeakMaps() CC_TELEMETRY(_OOM, true); } } // Flood black from any objects in the purple buffer that are in the CC graph. class PurpleScanBlackVisitor { public: - PurpleScanBlackVisitor(GCGraph &aGraph, uint32_t &aCount, bool &aFailed) - : mGraph(aGraph), mCount(aCount), mFailed(aFailed) + PurpleScanBlackVisitor(GCGraph &aGraph, nsICycleCollectorListener *aListener, + uint32_t &aCount, bool &aFailed) + : mGraph(aGraph), mListener(aListener), mCount(aCount), mFailed(aFailed) { } void Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry) { MOZ_ASSERT(aEntry->mObject, "Entries with null mObject shouldn't be in the purple buffer."); MOZ_ASSERT(aEntry->mRefCnt->get() != 0, "Snow-white objects shouldn't be in the purple buffer."); @@ -2724,24 +2733,28 @@ public: MOZ_ASSERT(obj, "Don't add objects that don't participate in collection!"); } PtrInfo *pi = mGraph.FindNode(obj); if (!pi) { return; } MOZ_ASSERT(pi->mParticipant, "No dead objects should be in the purple buffer."); + if (MOZ_UNLIKELY(mListener)) { + mListener->NoteIncrementalRoot((uint64_t)pi->mPointer); + } if (pi->mColor == black) { return; } GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mCount, mFailed)).Walk(pi); } private: GCGraph &mGraph; + nsICycleCollectorListener *mListener; uint32_t &mCount; bool &mFailed; }; // Objects that have been stored somewhere since the start of incremental graph building must // be treated as live for this cycle collection, because we may not have accurate information // about who holds references to them. void @@ -2754,50 +2767,69 @@ nsCycleCollector::ScanIncrementalRoots() // refcounted object is purple, it may have been AddRef'd during the current // ICC. (It may also have only been released.) If that is the case, we cannot // be sure that the set of things pointing to the object in the CC graph // is accurate. Therefore, for safety, we treat any purple objects as being // live during the current CC. We don't remove anything from the purple // buffer here, so these objects will be suspected and freed in the next CC // if they are garbage. bool failed = false; - PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mWhiteNodeCount, failed); + PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mListener, mWhiteNodeCount, failed); mPurpleBuf.VisitEntries(purpleScanBlackVisitor); timeLog.Checkpoint("ScanIncrementalRoots::fix purple"); // Garbage collected objects: // If a GCed object was added to the graph with a refcount of zero, and is // now marked black by the GC, it was probably gray before and was exposed // to active JS, so it may have been stored somewhere, so it needs to be // treated as live. if (mJSRuntime) { nsCycleCollectionParticipant *jsParticipant = mJSRuntime->GCThingParticipant(); nsCycleCollectionParticipant *zoneParticipant = mJSRuntime->ZoneParticipant(); NodePool::Enumerator etor(mGraph.mNodes); while (!etor.IsDone()) { PtrInfo *pi = etor.GetNext(); - if (pi->mRefCount != 0 || pi->mColor == black) { + // If the refcount is non-zero, pi can't have been a gray JS object. + if (pi->mRefCount != 0) { continue; } + // As an optimization, if an object has already been determined to be live, + // don't consider it further. We can't do this if there is a listener, + // because the listener wants to know the complete set of incremental roots. + if (pi->mColor == black && MOZ_LIKELY(!mListener)) { + continue; + } + + // If the object is still marked gray by the GC, nothing could have gotten + // hold of it, so it isn't an incremental root. if (pi->mParticipant == jsParticipant) { if (xpc_GCThingIsGrayCCThing(pi->mPointer)) { continue; } } else if (pi->mParticipant == zoneParticipant) { JS::Zone *zone = static_cast<JS::Zone*>(pi->mPointer); if (js::ZoneGlobalsAreAllGray(zone)) { continue; } } else { MOZ_ASSERT(false, "Non-JS thing with 0 refcount? Treating as live."); } + // At this point, pi must be an incremental root. + + // If there's a listener, tell it about this root. We don't bother with the + // optimization of skipping the Walk() if pi is black: it will just return + // without doing anything and there's no need to make this case faster. + if (MOZ_UNLIKELY(mListener)) { + mListener->NoteIncrementalRoot((uint64_t)pi->mPointer); + } + GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mWhiteNodeCount, failed)).Walk(pi); } timeLog.Checkpoint("ScanIncrementalRoots::fix JS"); } if (failed) { NS_ASSERTION(false, "Ran out of memory in ScanIncrementalRoots");
--- a/xpcom/base/nsICycleCollectorListener.idl +++ b/xpcom/base/nsICycleCollectorListener.idl @@ -21,26 +21,27 @@ interface nsICycleCollectorHandler : nsI in unsigned long aKnownEdges); void describeGarbage(in ACString aAddress); }; /** Interface to pass to the cycle collector to get information about * the CC graph while it's being built. The order of calls will be a * call to begin(); then for every node in the graph a call to either * noteRefCountedObject() or noteGCedObject(), followed by calls to - * noteEdge() for every edge starting at that node; then a call to - * beginResults(); then a mixture of describeRoot() for ref counted + * noteEdge() for every edge starting at that node. Then, there may + * be calls to noteIncrementalRoot(). After that, beginResults() will + * be called, followed by a mixture of describeRoot() for ref counted * nodes the CC has identified as roots and describeGarbage() for * nodes the CC has identified as garbage. Ref counted nodes that are * not identified as either roots or garbage are neither, and have a * known edges count equal to their ref count. Finally, there will be * a call to end(). If begin() returns an error none of the other * functions will be called. */ -[scriptable, builtinclass, uuid(786215b7-1e4b-433b-b617-96b176273601)] +[scriptable, builtinclass, uuid(2d04dd00-abc4-11e3-a5e2-0800200c9a66)] interface nsICycleCollectorListener : nsISupports { nsICycleCollectorListener allTraces(); // false if allTraces() has not been called. readonly attribute boolean wantAllTraces; // The default implementation of this interface will print out // a log to a file unless disableLog is set to true. @@ -65,16 +66,21 @@ interface nsICycleCollectorListener : ns in string aObjectDescription, in unsigned long long aCompartmentAddress); void noteEdge(in unsigned long long aToAddress, in string aEdgeName); void noteWeakMapEntry(in unsigned long long aMap, in unsigned long long aKey, in unsigned long long aKeyDelegate, in unsigned long long aValue); + // An "incremental root" is an object that may have had a new + // reference to it created during an incremental collection, + // and must therefore be treated as live for safety. + void noteIncrementalRoot(in unsigned long long aAddress); + void beginResults(); void describeRoot(in unsigned long long aAddress, in unsigned long aKnownEdges); void describeGarbage(in unsigned long long aAddress); void end(); // Returns false if there isn't anything more to process. boolean processNext(in nsICycleCollectorHandler aHandler);