|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 module.metadata = { |
|
7 "stability": "deprecated" |
|
8 }; |
|
9 |
|
10 const { Cc, Ci, Cu, components } = require("chrome"); |
|
11 const { when: unload } = require("../system/unload") |
|
12 |
|
13 var trackedObjects = {}; |
|
14 const Compacter = { |
|
15 notify: function() { |
|
16 var newTrackedObjects = {}; |
|
17 |
|
18 for (let name in trackedObjects) { |
|
19 let oldBin = trackedObjects[name]; |
|
20 let newBin = []; |
|
21 let strongRefs = []; |
|
22 |
|
23 for (let i = 0, l = oldBin.length; i < l; i++) { |
|
24 let strongRef = oldBin[i].weakref.get(); |
|
25 |
|
26 if (strongRef && strongRefs.indexOf(strongRef) == -1) { |
|
27 strongRefs.push(strongRef); |
|
28 newBin.push(oldBin[i]); |
|
29 } |
|
30 } |
|
31 |
|
32 if (newBin.length) |
|
33 newTrackedObjects[name] = newBin; |
|
34 } |
|
35 |
|
36 trackedObjects = newTrackedObjects; |
|
37 } |
|
38 }; |
|
39 |
|
40 var timer = Cc["@mozilla.org/timer;1"] |
|
41 .createInstance(Ci.nsITimer); |
|
42 timer.initWithCallback(Compacter, |
|
43 5000, |
|
44 Ci.nsITimer.TYPE_REPEATING_SLACK); |
|
45 |
|
46 function track(object, bin, stackFrameNumber) { |
|
47 var frame = components.stack.caller; |
|
48 var weakref = Cu.getWeakReference(object); |
|
49 |
|
50 if (!bin && 'constructor' in object) |
|
51 bin = object.constructor.name; |
|
52 if (bin == "Object") |
|
53 bin = frame.name; |
|
54 if (!bin) |
|
55 bin = "generic"; |
|
56 if (!(bin in trackedObjects)) |
|
57 trackedObjects[bin] = []; |
|
58 |
|
59 if (stackFrameNumber > 0) |
|
60 for (var i = 0; i < stackFrameNumber; i++) |
|
61 frame = frame.caller; |
|
62 |
|
63 trackedObjects[bin].push({weakref: weakref, |
|
64 created: new Date(), |
|
65 filename: frame.filename, |
|
66 lineNo: frame.lineNumber, |
|
67 bin: bin}); |
|
68 } |
|
69 exports.track = track; |
|
70 |
|
71 var getBins = exports.getBins = function getBins() { |
|
72 var names = []; |
|
73 for (let name in trackedObjects) |
|
74 names.push(name); |
|
75 return names; |
|
76 }; |
|
77 |
|
78 function getObjects(bin) { |
|
79 var results = []; |
|
80 |
|
81 function getLiveObjectsInBin(bin) { |
|
82 for (let i = 0, l = bin.length; i < l; i++) { |
|
83 let object = bin[i].weakref.get(); |
|
84 |
|
85 if (object) { |
|
86 results.push(bin[i]); |
|
87 } |
|
88 } |
|
89 } |
|
90 |
|
91 if (bin) { |
|
92 if (bin in trackedObjects) |
|
93 getLiveObjectsInBin(trackedObjects[bin]); |
|
94 } |
|
95 else { |
|
96 for (let name in trackedObjects) |
|
97 getLiveObjectsInBin(trackedObjects[name]); |
|
98 } |
|
99 |
|
100 return results; |
|
101 } |
|
102 exports.getObjects = getObjects; |
|
103 |
|
104 function gc() { |
|
105 // Components.utils.forceGC() doesn't currently perform |
|
106 // cycle collection, which means that e.g. DOM elements |
|
107 // won't be collected by it. Fortunately, there are |
|
108 // other ways... |
|
109 var test_utils = Cc["@mozilla.org/appshell/appShellService;1"] |
|
110 .getService(Ci.nsIAppShellService) |
|
111 .hiddenDOMWindow |
|
112 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
113 .getInterface(Ci.nsIDOMWindowUtils); |
|
114 test_utils.garbageCollect(); |
|
115 // Clean metadata for dead objects |
|
116 Compacter.notify(); |
|
117 // Not sure why, but sometimes it appears that we don't get |
|
118 // them all with just one CC, so let's do it again. |
|
119 test_utils.garbageCollect(); |
|
120 }; |
|
121 exports.gc = gc; |
|
122 |
|
123 unload(_ => { |
|
124 trackedObjects = {}; |
|
125 if (timer) { |
|
126 timer.cancel(); |
|
127 timer = null; |
|
128 } |
|
129 }); |