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