|
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 file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 function CCAnalyzer() { |
|
6 } |
|
7 |
|
8 CCAnalyzer.prototype = { |
|
9 clear: function () { |
|
10 this.callback = null; |
|
11 this.processingCount = 0; |
|
12 this.graph = {}; |
|
13 this.roots = []; |
|
14 this.garbage = []; |
|
15 this.edges = []; |
|
16 this.listener = null; |
|
17 }, |
|
18 |
|
19 run: function (aCallback) { |
|
20 this.clear(); |
|
21 this.callback = aCallback; |
|
22 |
|
23 this.listener = Cc["@mozilla.org/cycle-collector-logger;1"]. |
|
24 createInstance(Ci.nsICycleCollectorListener); |
|
25 |
|
26 this.listener.disableLog = true; |
|
27 this.listener.wantAfterProcessing = true; |
|
28 |
|
29 this.runCC(3); |
|
30 }, |
|
31 |
|
32 runCC: function (aCounter) { |
|
33 let utils = window.QueryInterface(Ci.nsIInterfaceRequestor). |
|
34 getInterface(Ci.nsIDOMWindowUtils); |
|
35 |
|
36 if (aCounter > 1) { |
|
37 utils.garbageCollect(); |
|
38 setTimeout(this.runCC.bind(this, aCounter - 1), 0); |
|
39 } else { |
|
40 utils.garbageCollect(this.listener); |
|
41 this.processLog(); |
|
42 } |
|
43 }, |
|
44 |
|
45 processLog: function () { |
|
46 // Process entire heap step by step in 5K chunks |
|
47 for (let i = 0; i < 5000; i++) { |
|
48 if (!this.listener.processNext(this)) { |
|
49 this.callback(); |
|
50 this.clear(); |
|
51 return; |
|
52 } |
|
53 } |
|
54 |
|
55 // Next chunk on timeout. |
|
56 setTimeout(this.processLog.bind(this), 0); |
|
57 }, |
|
58 |
|
59 noteRefCountedObject: function (aAddress, aRefCount, aObjectDescription) { |
|
60 let o = this.ensureObject(aAddress); |
|
61 o.address = aAddress; |
|
62 o.refcount = aRefCount; |
|
63 o.name = aObjectDescription; |
|
64 }, |
|
65 |
|
66 noteGCedObject: function (aAddress, aMarked, aObjectDescription) { |
|
67 let o = this.ensureObject(aAddress); |
|
68 o.address = aAddress; |
|
69 o.gcmarked = aMarked; |
|
70 o.name = aObjectDescription; |
|
71 }, |
|
72 |
|
73 noteEdge: function (aFromAddress, aToAddress, aEdgeName) { |
|
74 let fromObject = this.ensureObject(aFromAddress); |
|
75 let toObject = this.ensureObject(aToAddress); |
|
76 fromObject.edges.push({name: aEdgeName, to: toObject}); |
|
77 toObject.owners.push({name: aEdgeName, from: fromObject}); |
|
78 |
|
79 this.edges.push({ |
|
80 name: aEdgeName, |
|
81 from: fromObject, |
|
82 to: toObject |
|
83 }); |
|
84 }, |
|
85 |
|
86 describeRoot: function (aAddress, aKnownEdges) { |
|
87 let o = this.ensureObject(aAddress); |
|
88 o.root = true; |
|
89 o.knownEdges = aKnownEdges; |
|
90 this.roots.push(o); |
|
91 }, |
|
92 |
|
93 describeGarbage: function (aAddress) { |
|
94 let o = this.ensureObject(aAddress); |
|
95 o.garbage = true; |
|
96 this.garbage.push(o); |
|
97 }, |
|
98 |
|
99 ensureObject: function (aAddress) { |
|
100 if (!this.graph[aAddress]) |
|
101 this.graph[aAddress] = new CCObject(); |
|
102 |
|
103 return this.graph[aAddress]; |
|
104 }, |
|
105 |
|
106 find: function (aText) { |
|
107 let result = []; |
|
108 for each (let o in this.graph) { |
|
109 if (!o.garbage && o.name.indexOf(aText) >= 0) |
|
110 result.push(o); |
|
111 } |
|
112 return result; |
|
113 } |
|
114 }; |
|
115 |
|
116 function CCObject() { |
|
117 this.name = ""; |
|
118 this.address = null; |
|
119 this.refcount = 0; |
|
120 this.gcmarked = false; |
|
121 this.root = false; |
|
122 this.garbage = false; |
|
123 this.knownEdges = 0; |
|
124 this.edges = []; |
|
125 this.owners = []; |
|
126 } |