1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
/* 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"
%{C++
#include <stdio.h>
%}
[ptr] native FILE(FILE);
interface nsIFile;
/**
* 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);
};
/**
* This interface allows replacing the log-writing backend for an
* nsICycleCollectorListener. As this interface is also called while
* the cycle collector is running, it cannot be implemented in JS.
*/
[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
interface nsICycleCollectorLogSink : nsISupports
{
[noscript] void open(out FILE aGCLog, out FILE aCCLog);
void closeGCLog();
void closeCCLog();
// This string will appear somewhere in the log's filename.
attribute AString filenameIdentifier;
// This is the process ID; it can be changed if logging is on behalf
// of another process.
attribute int32_t processIdentifier;
// The GC log file, if logging to files.
readonly attribute nsIFile gcLog;
// The CC log file, if logging to files.
readonly attribute nsIFile ccLog;
};
/**
* 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 is running. 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(c7d55656-e0d8-4986-88bb-cb28cb55b993)]
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;
// If |disableLog| is false, this object will be sent the log text.
attribute nsICycleCollectorLogSink logSink;
// If true, record all method calls in memory, to be retrieved later
// using |processNext|. Initially false.
attribute boolean wantAfterProcessing;
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);
};
|