Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #include "nsISupports.idl" |
michael@0 | 6 | |
michael@0 | 7 | /** |
michael@0 | 8 | * Interfaces for observing the cycle collector's work, both from C++ and |
michael@0 | 9 | * from JavaScript. |
michael@0 | 10 | * |
michael@0 | 11 | * If given an object implementing nsICycleCollectorListener, the cycle |
michael@0 | 12 | * collector calls that object's methods as it works, describing the |
michael@0 | 13 | * objects it visits, the edges it finds, and the conclusions it reaches |
michael@0 | 14 | * about which objects are live. |
michael@0 | 15 | * |
michael@0 | 16 | * Analyzing cycle collection from JS is harder: an nsICycleCollectorListener |
michael@0 | 17 | * mustn't mess with the object graph while the cycle collector is trying to |
michael@0 | 18 | * figure it out, which means it can't be implemented by JS code: JS can't do |
michael@0 | 19 | * much of anything useful within those constraints. Instead, JS code can |
michael@0 | 20 | * instantiate @mozilla.org/cycle-collector-logger;1, a C++ class implementing |
michael@0 | 21 | * nsICycleCollectorListener that logs the cycle collector's mumblings and then |
michael@0 | 22 | * replays them later to an nsICycleCollectorHandler --- which *can* be |
michael@0 | 23 | * implemented in JS. |
michael@0 | 24 | */ |
michael@0 | 25 | |
michael@0 | 26 | /** |
michael@0 | 27 | * The interface JS code should implement to receive annotations logged by an |
michael@0 | 28 | * @mozilla.org/cycle-collector-logger;1 instance. Pass an instance of this to |
michael@0 | 29 | * the logger's 'processNext' method. |
michael@0 | 30 | * |
michael@0 | 31 | * The methods are a subset of those in nsICycleCollectorListener; see the |
michael@0 | 32 | * descriptions there. |
michael@0 | 33 | */ |
michael@0 | 34 | [scriptable, uuid(39a8f80e-7eee-4141-b9ef-6e2a7d6e466d)] |
michael@0 | 35 | interface nsICycleCollectorHandler : nsISupports |
michael@0 | 36 | { |
michael@0 | 37 | void noteRefCountedObject(in ACString aAddress, |
michael@0 | 38 | in unsigned long aRefCount, |
michael@0 | 39 | in ACString aObjectDescription); |
michael@0 | 40 | void noteGCedObject(in ACString aAddress, |
michael@0 | 41 | in boolean aMarked, |
michael@0 | 42 | in ACString aObjectDescription, |
michael@0 | 43 | in ACString aCompartmentAddress); |
michael@0 | 44 | void noteEdge(in ACString aFromAddress, |
michael@0 | 45 | in ACString aToAddress, |
michael@0 | 46 | in ACString aEdgeName); |
michael@0 | 47 | void describeRoot(in ACString aAddress, |
michael@0 | 48 | in unsigned long aKnownEdges); |
michael@0 | 49 | void describeGarbage(in ACString aAddress); |
michael@0 | 50 | }; |
michael@0 | 51 | |
michael@0 | 52 | /** |
michael@0 | 53 | * Given an instance of this interface, the cycle collector calls the instance's |
michael@0 | 54 | * methods to report the objects it visits, the edges between them, and its |
michael@0 | 55 | * conclusions about which objects are roots and which are garbage. |
michael@0 | 56 | * |
michael@0 | 57 | * For a single cycle collection pass, the cycle collector calls this |
michael@0 | 58 | * interface's methods in the following order: |
michael@0 | 59 | * |
michael@0 | 60 | * - First, |begin|. If |begin| returns an error, none of the listener's other |
michael@0 | 61 | * methods will be called. |
michael@0 | 62 | * |
michael@0 | 63 | * - Then, for each node in the graph: |
michael@0 | 64 | * - a call to either |noteRefCountedObject| or |noteGCedObject|, to describe |
michael@0 | 65 | * the node itself; and |
michael@0 | 66 | * - for each edge starting at that node, a call to |noteEdge|. |
michael@0 | 67 | * |
michael@0 | 68 | * - Then, zero or more calls to |noteIncrementalRoot|; an "incremental |
michael@0 | 69 | * root" is an object that may have had a new reference to it created |
michael@0 | 70 | * during an incremental collection, and must therefore be treated as |
michael@0 | 71 | * live for safety. |
michael@0 | 72 | * |
michael@0 | 73 | * - After all the nodes have been described, a call to |beginResults|. |
michael@0 | 74 | * |
michael@0 | 75 | * - A series of calls to: |
michael@0 | 76 | * - |describeRoot|, for reference-counted nodes that the CC has identified as |
michael@0 | 77 | * roots of collection. (The cycle collector didn't find enough incoming |
michael@0 | 78 | * edges to account for these nodes' reference counts, so there must be code |
michael@0 | 79 | * holding on to them that the cycle collector doesn't know about.) |
michael@0 | 80 | * - |describeGarbage|, for nodes the cycle collector has identified as garbage. |
michael@0 | 81 | * |
michael@0 | 82 | * Any node not mentioned in a call to |describeRoot| or |describeGarbage| is |
michael@0 | 83 | * neither a root nor garbage. (The cycle collector was able to find all the |
michael@0 | 84 | * edges implied by the node's reference count.) |
michael@0 | 85 | * |
michael@0 | 86 | * - Finally, a call to |end|. |
michael@0 | 87 | * |
michael@0 | 88 | * |
michael@0 | 89 | * This interface cannot be implemented by JavaScript code, as it is called |
michael@0 | 90 | * while the cycle collector works. To analyze cycle collection data in JS: |
michael@0 | 91 | * |
michael@0 | 92 | * - Create an instance of @mozilla.org/cycle-collector-logger;1, which |
michael@0 | 93 | * implements this interface. |
michael@0 | 94 | * |
michael@0 | 95 | * - Set its |disableLog| property to true. This prevents the logger from |
michael@0 | 96 | * printing messages about each method call to a temporary log file. |
michael@0 | 97 | * |
michael@0 | 98 | * - Set its |wantAfterProcessing| property to true. This tells the logger |
michael@0 | 99 | * to record calls to its methods in memory. The |processNext| method |
michael@0 | 100 | * returns events from this record. |
michael@0 | 101 | * |
michael@0 | 102 | * - Perform a collection using the logger. For example, call |
michael@0 | 103 | * |nsIDOMWindowUtils|'s |garbageCollect| method, passing the logger as |
michael@0 | 104 | * the |aListener| argument. |
michael@0 | 105 | * |
michael@0 | 106 | * - When the collection is complete, loop calling the logger's |
michael@0 | 107 | * |processNext| method, passing a JavaScript object that implements |
michael@0 | 108 | * nsICycleCollectorHandler. This JS code is free to allocate and operate |
michael@0 | 109 | * on objects however it pleases: the cycle collector has finished its |
michael@0 | 110 | * work, and the JS code is simply consuming recorded data. |
michael@0 | 111 | */ |
michael@0 | 112 | [scriptable, builtinclass, uuid(c46e6947-9076-4a0e-bb27-d4aa3706c54d)] |
michael@0 | 113 | interface nsICycleCollectorListener : nsISupports |
michael@0 | 114 | { |
michael@0 | 115 | // Return a listener that directs the cycle collector to traverse |
michael@0 | 116 | // objects that it knows won't be collectable. |
michael@0 | 117 | // |
michael@0 | 118 | // Note that even this listener will not visit every node in the heap; |
michael@0 | 119 | // the cycle collector can't see the entire heap. But while this |
michael@0 | 120 | // listener is in use, the collector disables some optimizations it |
michael@0 | 121 | // normally uses to avoid certain classes of objects that are certainly |
michael@0 | 122 | // alive. So, if your purpose is to get a view of the portion of the |
michael@0 | 123 | // heap that is of interest to the cycle collector, and not simply find |
michael@0 | 124 | // garbage, then you should use the listener this returns. |
michael@0 | 125 | // |
michael@0 | 126 | // Note that this does not necessarily return a new listener; rather, it may |
michael@0 | 127 | // simply set a flag on this listener (a side effect!) and return it. |
michael@0 | 128 | nsICycleCollectorListener allTraces(); |
michael@0 | 129 | |
michael@0 | 130 | // True if this listener will behave like one returned by allTraces(). |
michael@0 | 131 | readonly attribute boolean wantAllTraces; |
michael@0 | 132 | |
michael@0 | 133 | // If true, do not log each method call to a temporary file. |
michael@0 | 134 | // Initially false. |
michael@0 | 135 | attribute boolean disableLog; |
michael@0 | 136 | |
michael@0 | 137 | // This string will appear somewhere in the log's filename. |
michael@0 | 138 | attribute AString filenameIdentifier; |
michael@0 | 139 | |
michael@0 | 140 | // If true, record all method calls in memory, to be retrieved later |
michael@0 | 141 | // using |processNext|. Initially false. |
michael@0 | 142 | attribute boolean wantAfterProcessing; |
michael@0 | 143 | |
michael@0 | 144 | // This string will indicate the full path of the GC log if enabled. |
michael@0 | 145 | readonly attribute AString gcLogPath; |
michael@0 | 146 | |
michael@0 | 147 | // This string will indicate the full path of the CC log if enabled. |
michael@0 | 148 | readonly attribute AString ccLogPath; |
michael@0 | 149 | |
michael@0 | 150 | void begin(); |
michael@0 | 151 | void noteRefCountedObject (in unsigned long long aAddress, |
michael@0 | 152 | in unsigned long aRefCount, |
michael@0 | 153 | in string aObjectDescription); |
michael@0 | 154 | void noteGCedObject (in unsigned long long aAddress, |
michael@0 | 155 | in boolean aMarked, |
michael@0 | 156 | in string aObjectDescription, |
michael@0 | 157 | in unsigned long long aCompartmentAddress); |
michael@0 | 158 | void noteEdge(in unsigned long long aToAddress, |
michael@0 | 159 | in string aEdgeName); |
michael@0 | 160 | void noteWeakMapEntry(in unsigned long long aMap, |
michael@0 | 161 | in unsigned long long aKey, |
michael@0 | 162 | in unsigned long long aKeyDelegate, |
michael@0 | 163 | in unsigned long long aValue); |
michael@0 | 164 | void noteIncrementalRoot(in unsigned long long aAddress); |
michael@0 | 165 | |
michael@0 | 166 | void beginResults(); |
michael@0 | 167 | void describeRoot(in unsigned long long aAddress, |
michael@0 | 168 | in unsigned long aKnownEdges); |
michael@0 | 169 | void describeGarbage(in unsigned long long aAddress); |
michael@0 | 170 | void end(); |
michael@0 | 171 | |
michael@0 | 172 | // Report the next recorded event to |aHandler|, and remove it from the |
michael@0 | 173 | // record. Return false if there isn't anything more to process. |
michael@0 | 174 | // |
michael@0 | 175 | // Note that we only record events to report here if our |
michael@0 | 176 | // |wantAfterProcessing| property is true. |
michael@0 | 177 | boolean processNext(in nsICycleCollectorHandler aHandler); |
michael@0 | 178 | }; |