diff -r 000000000000 -r 6474c204b198 xpcom/base/nsICycleCollectorListener.idl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xpcom/base/nsICycleCollectorListener.idl Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,178 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * Interfaces for observing the cycle collector's work, both from C++ and + * from JavaScript. + * + * If given an object implementing nsICycleCollectorListener, the cycle + * collector calls that object's methods as it works, describing the + * objects it visits, the edges it finds, and the conclusions it reaches + * about which objects are live. + * + * Analyzing cycle collection from JS is harder: an nsICycleCollectorListener + * mustn't mess with the object graph while the cycle collector is trying to + * figure it out, which means it can't be implemented by JS code: JS can't do + * much of anything useful within those constraints. Instead, JS code can + * instantiate @mozilla.org/cycle-collector-logger;1, a C++ class implementing + * nsICycleCollectorListener that logs the cycle collector's mumblings and then + * replays them later to an nsICycleCollectorHandler --- which *can* be + * implemented in JS. + */ + +/** + * The interface JS code should implement to receive annotations logged by an + * @mozilla.org/cycle-collector-logger;1 instance. Pass an instance of this to + * the logger's 'processNext' method. + * + * The methods are a subset of those in nsICycleCollectorListener; see the + * descriptions there. + */ +[scriptable, uuid(39a8f80e-7eee-4141-b9ef-6e2a7d6e466d)] +interface nsICycleCollectorHandler : nsISupports +{ + void noteRefCountedObject(in ACString aAddress, + in unsigned long aRefCount, + in ACString aObjectDescription); + void noteGCedObject(in ACString aAddress, + in boolean aMarked, + in ACString aObjectDescription, + in ACString aCompartmentAddress); + void noteEdge(in ACString aFromAddress, + in ACString aToAddress, + in ACString aEdgeName); + void describeRoot(in ACString aAddress, + in unsigned long aKnownEdges); + void describeGarbage(in ACString aAddress); +}; + +/** + * Given an instance of this interface, the cycle collector calls the instance's + * methods to report the objects it visits, the edges between them, and its + * conclusions about which objects are roots and which are garbage. + * + * For a single cycle collection pass, the cycle collector calls this + * interface's methods in the following order: + * + * - First, |begin|. If |begin| returns an error, none of the listener's other + * methods will be called. + * + * - Then, for each node in the graph: + * - a call to either |noteRefCountedObject| or |noteGCedObject|, to describe + * the node itself; and + * - for each edge starting at that node, a call to |noteEdge|. + * + * - Then, zero or more calls to |noteIncrementalRoot|; 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. + * + * - After all the nodes have been described, a call to |beginResults|. + * + * - A series of calls to: + * - |describeRoot|, for reference-counted nodes that the CC has identified as + * roots of collection. (The cycle collector didn't find enough incoming + * edges to account for these nodes' reference counts, so there must be code + * holding on to them that the cycle collector doesn't know about.) + * - |describeGarbage|, for nodes the cycle collector has identified as garbage. + * + * Any node not mentioned in a call to |describeRoot| or |describeGarbage| is + * neither a root nor garbage. (The cycle collector was able to find all the + * edges implied by the node's reference count.) + * + * - Finally, a call to |end|. + * + * + * This interface cannot be implemented by JavaScript code, as it is called + * while the cycle collector works. To analyze cycle collection data in JS: + * + * - Create an instance of @mozilla.org/cycle-collector-logger;1, which + * implements this interface. + * + * - Set its |disableLog| property to true. This prevents the logger from + * printing messages about each method call to a temporary log file. + * + * - Set its |wantAfterProcessing| property to true. This tells the logger + * to record calls to its methods in memory. The |processNext| method + * returns events from this record. + * + * - Perform a collection using the logger. For example, call + * |nsIDOMWindowUtils|'s |garbageCollect| method, passing the logger as + * the |aListener| argument. + * + * - When the collection is complete, loop calling the logger's + * |processNext| method, passing a JavaScript object that implements + * nsICycleCollectorHandler. This JS code is free to allocate and operate + * on objects however it pleases: the cycle collector has finished its + * work, and the JS code is simply consuming recorded data. + */ +[scriptable, builtinclass, uuid(c46e6947-9076-4a0e-bb27-d4aa3706c54d)] +interface nsICycleCollectorListener : nsISupports +{ + // Return a listener that directs the cycle collector to traverse + // objects that it knows won't be collectable. + // + // Note that even this listener will not visit every node in the heap; + // the cycle collector can't see the entire heap. But while this + // listener is in use, the collector disables some optimizations it + // normally uses to avoid certain classes of objects that are certainly + // alive. So, if your purpose is to get a view of the portion of the + // heap that is of interest to the cycle collector, and not simply find + // garbage, then you should use the listener this returns. + // + // Note that this does not necessarily return a new listener; rather, it may + // simply set a flag on this listener (a side effect!) and return it. + nsICycleCollectorListener allTraces(); + + // True if this listener will behave like one returned by allTraces(). + readonly attribute boolean wantAllTraces; + + // If true, do not log each method call to a temporary file. + // Initially false. + attribute boolean disableLog; + + // This string will appear somewhere in the log's filename. + attribute AString filenameIdentifier; + + // If true, record all method calls in memory, to be retrieved later + // using |processNext|. Initially false. + attribute boolean wantAfterProcessing; + + // This string will indicate the full path of the GC log if enabled. + readonly attribute AString gcLogPath; + + // This string will indicate the full path of the CC log if enabled. + readonly attribute AString ccLogPath; + + void begin(); + void noteRefCountedObject (in unsigned long long aAddress, + in unsigned long aRefCount, + in string aObjectDescription); + void noteGCedObject (in unsigned long long aAddress, + in boolean aMarked, + 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); + 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(); + + // Report the next recorded event to |aHandler|, and remove it from the + // record. Return false if there isn't anything more to process. + // + // Note that we only record events to report here if our + // |wantAfterProcessing| property is true. + boolean processNext(in nsICycleCollectorHandler aHandler); +};