OLD | NEW |
1 // Copyright 2009 the V8 project authors. All rights reserved. | 1 // Copyright 2009 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
11 // with the distribution. | 11 // with the distribution. |
12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
15 // | 15 // |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 | 28 |
29 // Initlialize namespaces | |
30 var devtools = devtools || {}; | |
31 devtools.profiler = devtools.profiler || {}; | |
32 | |
33 | |
34 /** | 29 /** |
35 * Creates a profile object for processing profiling-related events | 30 * Creates a profile object for processing profiling-related events |
36 * and calculating function execution times. | 31 * and calculating function execution times. |
37 * | 32 * |
38 * @constructor | 33 * @constructor |
39 */ | 34 */ |
40 devtools.profiler.Profile = function() { | 35 function Profile() { |
41 this.codeMap_ = new devtools.profiler.CodeMap(); | 36 this.codeMap_ = new CodeMap(); |
42 this.topDownTree_ = new devtools.profiler.CallTree(); | 37 this.topDownTree_ = new CallTree(); |
43 this.bottomUpTree_ = new devtools.profiler.CallTree(); | 38 this.bottomUpTree_ = new CallTree(); |
44 }; | 39 }; |
45 | 40 |
46 /** | 41 /** |
47 * Version of profiler log. | 42 * Version of profiler log. |
48 */ | 43 */ |
49 devtools.profiler.Profile.VERSION = 2; | 44 Profile.VERSION = 2; |
50 | 45 |
51 | 46 |
52 /** | 47 /** |
53 * Returns whether a function with the specified name must be skipped. | 48 * Returns whether a function with the specified name must be skipped. |
54 * Should be overriden by subclasses. | 49 * Should be overriden by subclasses. |
55 * | 50 * |
56 * @param {string} name Function name. | 51 * @param {string} name Function name. |
57 */ | 52 */ |
58 devtools.profiler.Profile.prototype.skipThisFunction = function(name) { | 53 Profile.prototype.skipThisFunction = function(name) { |
59 return false; | 54 return false; |
60 }; | 55 }; |
61 | 56 |
62 | 57 |
63 /** | 58 /** |
64 * Enum for profiler operations that involve looking up existing | 59 * Enum for profiler operations that involve looking up existing |
65 * code entries. | 60 * code entries. |
66 * | 61 * |
67 * @enum {number} | 62 * @enum {number} |
68 */ | 63 */ |
69 devtools.profiler.Profile.Operation = { | 64 Profile.Operation = { |
70 MOVE: 0, | 65 MOVE: 0, |
71 DELETE: 1, | 66 DELETE: 1, |
72 TICK: 2 | 67 TICK: 2 |
73 }; | 68 }; |
74 | 69 |
75 | 70 |
76 /** | 71 /** |
77 * Called whenever the specified operation has failed finding a function | 72 * Called whenever the specified operation has failed finding a function |
78 * containing the specified address. Should be overriden by subclasses. | 73 * containing the specified address. Should be overriden by subclasses. |
79 * See the devtools.profiler.Profile.Operation enum for the list of | 74 * See the Profile.Operation enum for the list of |
80 * possible operations. | 75 * possible operations. |
81 * | 76 * |
82 * @param {number} operation Operation. | 77 * @param {number} operation Operation. |
83 * @param {number} addr Address of the unknown code. | 78 * @param {number} addr Address of the unknown code. |
84 * @param {number} opt_stackPos If an unknown address is encountered | 79 * @param {number} opt_stackPos If an unknown address is encountered |
85 * during stack strace processing, specifies a position of the frame | 80 * during stack strace processing, specifies a position of the frame |
86 * containing the address. | 81 * containing the address. |
87 */ | 82 */ |
88 devtools.profiler.Profile.prototype.handleUnknownCode = function( | 83 Profile.prototype.handleUnknownCode = function( |
89 operation, addr, opt_stackPos) { | 84 operation, addr, opt_stackPos) { |
90 }; | 85 }; |
91 | 86 |
92 | 87 |
93 /** | 88 /** |
94 * Registers a library. | 89 * Registers a library. |
95 * | 90 * |
96 * @param {string} name Code entry name. | 91 * @param {string} name Code entry name. |
97 * @param {number} startAddr Starting address. | 92 * @param {number} startAddr Starting address. |
98 * @param {number} endAddr Ending address. | 93 * @param {number} endAddr Ending address. |
99 */ | 94 */ |
100 devtools.profiler.Profile.prototype.addLibrary = function( | 95 Profile.prototype.addLibrary = function( |
101 name, startAddr, endAddr) { | 96 name, startAddr, endAddr) { |
102 var entry = new devtools.profiler.CodeMap.CodeEntry( | 97 var entry = new CodeMap.CodeEntry( |
103 endAddr - startAddr, name); | 98 endAddr - startAddr, name); |
104 this.codeMap_.addLibrary(startAddr, entry); | 99 this.codeMap_.addLibrary(startAddr, entry); |
105 return entry; | 100 return entry; |
106 }; | 101 }; |
107 | 102 |
108 | 103 |
109 /** | 104 /** |
110 * Registers statically compiled code entry. | 105 * Registers statically compiled code entry. |
111 * | 106 * |
112 * @param {string} name Code entry name. | 107 * @param {string} name Code entry name. |
113 * @param {number} startAddr Starting address. | 108 * @param {number} startAddr Starting address. |
114 * @param {number} endAddr Ending address. | 109 * @param {number} endAddr Ending address. |
115 */ | 110 */ |
116 devtools.profiler.Profile.prototype.addStaticCode = function( | 111 Profile.prototype.addStaticCode = function( |
117 name, startAddr, endAddr) { | 112 name, startAddr, endAddr) { |
118 var entry = new devtools.profiler.CodeMap.CodeEntry( | 113 var entry = new CodeMap.CodeEntry( |
119 endAddr - startAddr, name); | 114 endAddr - startAddr, name); |
120 this.codeMap_.addStaticCode(startAddr, entry); | 115 this.codeMap_.addStaticCode(startAddr, entry); |
121 return entry; | 116 return entry; |
122 }; | 117 }; |
123 | 118 |
124 | 119 |
125 /** | 120 /** |
126 * Registers dynamic (JIT-compiled) code entry. | 121 * Registers dynamic (JIT-compiled) code entry. |
127 * | 122 * |
128 * @param {string} type Code entry type. | 123 * @param {string} type Code entry type. |
129 * @param {string} name Code entry name. | 124 * @param {string} name Code entry name. |
130 * @param {number} start Starting address. | 125 * @param {number} start Starting address. |
131 * @param {number} size Code entry size. | 126 * @param {number} size Code entry size. |
132 */ | 127 */ |
133 devtools.profiler.Profile.prototype.addCode = function( | 128 Profile.prototype.addCode = function( |
134 type, name, start, size) { | 129 type, name, start, size) { |
135 var entry = new devtools.profiler.Profile.DynamicCodeEntry(size, type, name); | 130 var entry = new Profile.DynamicCodeEntry(size, type, name); |
136 this.codeMap_.addCode(start, entry); | 131 this.codeMap_.addCode(start, entry); |
137 return entry; | 132 return entry; |
138 }; | 133 }; |
139 | 134 |
140 | 135 |
141 /** | 136 /** |
142 * Creates an alias entry for a code entry. | 137 * Creates an alias entry for a code entry. |
143 * | 138 * |
144 * @param {number} aliasAddr Alias address. | 139 * @param {number} aliasAddr Alias address. |
145 * @param {number} addr Code entry address. | 140 * @param {number} addr Code entry address. |
146 */ | 141 */ |
147 devtools.profiler.Profile.prototype.addCodeAlias = function( | 142 Profile.prototype.addCodeAlias = function( |
148 aliasAddr, addr) { | 143 aliasAddr, addr) { |
149 var entry = this.codeMap_.findDynamicEntryByStartAddress(addr); | 144 var entry = this.codeMap_.findDynamicEntryByStartAddress(addr); |
150 if (entry) { | 145 if (entry) { |
151 this.codeMap_.addCode(aliasAddr, entry); | 146 this.codeMap_.addCode(aliasAddr, entry); |
152 } | 147 } |
153 }; | 148 }; |
154 | 149 |
155 | 150 |
156 /** | 151 /** |
157 * Reports about moving of a dynamic code entry. | 152 * Reports about moving of a dynamic code entry. |
158 * | 153 * |
159 * @param {number} from Current code entry address. | 154 * @param {number} from Current code entry address. |
160 * @param {number} to New code entry address. | 155 * @param {number} to New code entry address. |
161 */ | 156 */ |
162 devtools.profiler.Profile.prototype.moveCode = function(from, to) { | 157 Profile.prototype.moveCode = function(from, to) { |
163 try { | 158 try { |
164 this.codeMap_.moveCode(from, to); | 159 this.codeMap_.moveCode(from, to); |
165 } catch (e) { | 160 } catch (e) { |
166 this.handleUnknownCode(devtools.profiler.Profile.Operation.MOVE, from); | 161 this.handleUnknownCode(Profile.Operation.MOVE, from); |
167 } | 162 } |
168 }; | 163 }; |
169 | 164 |
170 | 165 |
171 /** | 166 /** |
172 * Reports about deletion of a dynamic code entry. | 167 * Reports about deletion of a dynamic code entry. |
173 * | 168 * |
174 * @param {number} start Starting address. | 169 * @param {number} start Starting address. |
175 */ | 170 */ |
176 devtools.profiler.Profile.prototype.deleteCode = function(start) { | 171 Profile.prototype.deleteCode = function(start) { |
177 try { | 172 try { |
178 this.codeMap_.deleteCode(start); | 173 this.codeMap_.deleteCode(start); |
179 } catch (e) { | 174 } catch (e) { |
180 this.handleUnknownCode(devtools.profiler.Profile.Operation.DELETE, start); | 175 this.handleUnknownCode(Profile.Operation.DELETE, start); |
181 } | 176 } |
182 }; | 177 }; |
183 | 178 |
184 | 179 |
185 /** | 180 /** |
186 * Reports about moving of a dynamic code entry. | 181 * Reports about moving of a dynamic code entry. |
187 * | 182 * |
188 * @param {number} from Current code entry address. | 183 * @param {number} from Current code entry address. |
189 * @param {number} to New code entry address. | 184 * @param {number} to New code entry address. |
190 */ | 185 */ |
191 devtools.profiler.Profile.prototype.safeMoveDynamicCode = function(from, to) { | 186 Profile.prototype.safeMoveDynamicCode = function(from, to) { |
192 if (this.codeMap_.findDynamicEntryByStartAddress(from)) { | 187 if (this.codeMap_.findDynamicEntryByStartAddress(from)) { |
193 this.codeMap_.moveCode(from, to); | 188 this.codeMap_.moveCode(from, to); |
194 } | 189 } |
195 }; | 190 }; |
196 | 191 |
197 | 192 |
198 /** | 193 /** |
199 * Reports about deletion of a dynamic code entry. | 194 * Reports about deletion of a dynamic code entry. |
200 * | 195 * |
201 * @param {number} start Starting address. | 196 * @param {number} start Starting address. |
202 */ | 197 */ |
203 devtools.profiler.Profile.prototype.safeDeleteDynamicCode = function(start) { | 198 Profile.prototype.safeDeleteDynamicCode = function(start) { |
204 if (this.codeMap_.findDynamicEntryByStartAddress(start)) { | 199 if (this.codeMap_.findDynamicEntryByStartAddress(start)) { |
205 this.codeMap_.deleteCode(start); | 200 this.codeMap_.deleteCode(start); |
206 } | 201 } |
207 }; | 202 }; |
208 | 203 |
209 | 204 |
210 /** | 205 /** |
211 * Retrieves a code entry by an address. | 206 * Retrieves a code entry by an address. |
212 * | 207 * |
213 * @param {number} addr Entry address. | 208 * @param {number} addr Entry address. |
214 */ | 209 */ |
215 devtools.profiler.Profile.prototype.findEntry = function(addr) { | 210 Profile.prototype.findEntry = function(addr) { |
216 return this.codeMap_.findEntry(addr); | 211 return this.codeMap_.findEntry(addr); |
217 }; | 212 }; |
218 | 213 |
219 | 214 |
220 /** | 215 /** |
221 * Records a tick event. Stack must contain a sequence of | 216 * Records a tick event. Stack must contain a sequence of |
222 * addresses starting with the program counter value. | 217 * addresses starting with the program counter value. |
223 * | 218 * |
224 * @param {Array<number>} stack Stack sample. | 219 * @param {Array<number>} stack Stack sample. |
225 */ | 220 */ |
226 devtools.profiler.Profile.prototype.recordTick = function(stack) { | 221 Profile.prototype.recordTick = function(stack) { |
227 var processedStack = this.resolveAndFilterFuncs_(stack); | 222 var processedStack = this.resolveAndFilterFuncs_(stack); |
228 this.bottomUpTree_.addPath(processedStack); | 223 this.bottomUpTree_.addPath(processedStack); |
229 processedStack.reverse(); | 224 processedStack.reverse(); |
230 this.topDownTree_.addPath(processedStack); | 225 this.topDownTree_.addPath(processedStack); |
231 }; | 226 }; |
232 | 227 |
233 | 228 |
234 /** | 229 /** |
235 * Translates addresses into function names and filters unneeded | 230 * Translates addresses into function names and filters unneeded |
236 * functions. | 231 * functions. |
237 * | 232 * |
238 * @param {Array<number>} stack Stack sample. | 233 * @param {Array<number>} stack Stack sample. |
239 */ | 234 */ |
240 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { | 235 Profile.prototype.resolveAndFilterFuncs_ = function(stack) { |
241 var result = []; | 236 var result = []; |
242 for (var i = 0; i < stack.length; ++i) { | 237 for (var i = 0; i < stack.length; ++i) { |
243 var entry = this.codeMap_.findEntry(stack[i]); | 238 var entry = this.codeMap_.findEntry(stack[i]); |
244 if (entry) { | 239 if (entry) { |
245 var name = entry.getName(); | 240 var name = entry.getName(); |
246 if (!this.skipThisFunction(name)) { | 241 if (!this.skipThisFunction(name)) { |
247 result.push(name); | 242 result.push(name); |
248 } | 243 } |
249 } else { | 244 } else { |
250 this.handleUnknownCode( | 245 this.handleUnknownCode( |
251 devtools.profiler.Profile.Operation.TICK, stack[i], i); | 246 Profile.Operation.TICK, stack[i], i); |
252 } | 247 } |
253 } | 248 } |
254 return result; | 249 return result; |
255 }; | 250 }; |
256 | 251 |
257 | 252 |
258 /** | 253 /** |
259 * Performs a BF traversal of the top down call graph. | 254 * Performs a BF traversal of the top down call graph. |
260 * | 255 * |
261 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 256 * @param {function(CallTree.Node)} f Visitor function. |
262 */ | 257 */ |
263 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { | 258 Profile.prototype.traverseTopDownTree = function(f) { |
264 this.topDownTree_.traverse(f); | 259 this.topDownTree_.traverse(f); |
265 }; | 260 }; |
266 | 261 |
267 | 262 |
268 /** | 263 /** |
269 * Performs a BF traversal of the bottom up call graph. | 264 * Performs a BF traversal of the bottom up call graph. |
270 * | 265 * |
271 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 266 * @param {function(CallTree.Node)} f Visitor function. |
272 */ | 267 */ |
273 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { | 268 Profile.prototype.traverseBottomUpTree = function(f) { |
274 this.bottomUpTree_.traverse(f); | 269 this.bottomUpTree_.traverse(f); |
275 }; | 270 }; |
276 | 271 |
277 | 272 |
278 /** | 273 /** |
279 * Calculates a top down profile for a node with the specified label. | 274 * Calculates a top down profile for a node with the specified label. |
280 * If no name specified, returns the whole top down calls tree. | 275 * If no name specified, returns the whole top down calls tree. |
281 * | 276 * |
282 * @param {string} opt_label Node label. | 277 * @param {string} opt_label Node label. |
283 */ | 278 */ |
284 devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_label) { | 279 Profile.prototype.getTopDownProfile = function(opt_label) { |
285 return this.getTreeProfile_(this.topDownTree_, opt_label); | 280 return this.getTreeProfile_(this.topDownTree_, opt_label); |
286 }; | 281 }; |
287 | 282 |
288 | 283 |
289 /** | 284 /** |
290 * Calculates a bottom up profile for a node with the specified label. | 285 * Calculates a bottom up profile for a node with the specified label. |
291 * If no name specified, returns the whole bottom up calls tree. | 286 * If no name specified, returns the whole bottom up calls tree. |
292 * | 287 * |
293 * @param {string} opt_label Node label. | 288 * @param {string} opt_label Node label. |
294 */ | 289 */ |
295 devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_label) { | 290 Profile.prototype.getBottomUpProfile = function(opt_label) { |
296 return this.getTreeProfile_(this.bottomUpTree_, opt_label); | 291 return this.getTreeProfile_(this.bottomUpTree_, opt_label); |
297 }; | 292 }; |
298 | 293 |
299 | 294 |
300 /** | 295 /** |
301 * Helper function for calculating a tree profile. | 296 * Helper function for calculating a tree profile. |
302 * | 297 * |
303 * @param {devtools.profiler.Profile.CallTree} tree Call tree. | 298 * @param {Profile.CallTree} tree Call tree. |
304 * @param {string} opt_label Node label. | 299 * @param {string} opt_label Node label. |
305 */ | 300 */ |
306 devtools.profiler.Profile.prototype.getTreeProfile_ = function(tree, opt_label)
{ | 301 Profile.prototype.getTreeProfile_ = function(tree, opt_label) { |
307 if (!opt_label) { | 302 if (!opt_label) { |
308 tree.computeTotalWeights(); | 303 tree.computeTotalWeights(); |
309 return tree; | 304 return tree; |
310 } else { | 305 } else { |
311 var subTree = tree.cloneSubtree(opt_label); | 306 var subTree = tree.cloneSubtree(opt_label); |
312 subTree.computeTotalWeights(); | 307 subTree.computeTotalWeights(); |
313 return subTree; | 308 return subTree; |
314 } | 309 } |
315 }; | 310 }; |
316 | 311 |
317 | 312 |
318 /** | 313 /** |
319 * Calculates a flat profile of callees starting from a node with | 314 * Calculates a flat profile of callees starting from a node with |
320 * the specified label. If no name specified, starts from the root. | 315 * the specified label. If no name specified, starts from the root. |
321 * | 316 * |
322 * @param {string} opt_label Starting node label. | 317 * @param {string} opt_label Starting node label. |
323 */ | 318 */ |
324 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_label) { | 319 Profile.prototype.getFlatProfile = function(opt_label) { |
325 var counters = new devtools.profiler.CallTree(); | 320 var counters = new CallTree(); |
326 var rootLabel = opt_label || devtools.profiler.CallTree.ROOT_NODE_LABEL; | 321 var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL; |
327 var precs = {}; | 322 var precs = {}; |
328 precs[rootLabel] = 0; | 323 precs[rootLabel] = 0; |
329 var root = counters.findOrAddChild(rootLabel); | 324 var root = counters.findOrAddChild(rootLabel); |
330 | 325 |
331 this.topDownTree_.computeTotalWeights(); | 326 this.topDownTree_.computeTotalWeights(); |
332 this.topDownTree_.traverseInDepth( | 327 this.topDownTree_.traverseInDepth( |
333 function onEnter(node) { | 328 function onEnter(node) { |
334 if (!(node.label in precs)) { | 329 if (!(node.label in precs)) { |
335 precs[node.label] = 0; | 330 precs[node.label] = 0; |
336 } | 331 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 | 366 |
372 | 367 |
373 /** | 368 /** |
374 * Creates a dynamic code entry. | 369 * Creates a dynamic code entry. |
375 * | 370 * |
376 * @param {number} size Code size. | 371 * @param {number} size Code size. |
377 * @param {string} type Code type. | 372 * @param {string} type Code type. |
378 * @param {string} name Function name. | 373 * @param {string} name Function name. |
379 * @constructor | 374 * @constructor |
380 */ | 375 */ |
381 devtools.profiler.Profile.DynamicCodeEntry = function(size, type, name) { | 376 Profile.DynamicCodeEntry = function(size, type, name) { |
382 devtools.profiler.CodeMap.CodeEntry.call(this, size, name); | 377 CodeMap.CodeEntry.call(this, size, name); |
383 this.type = type; | 378 this.type = type; |
384 }; | 379 }; |
385 | 380 |
386 | 381 |
387 /** | 382 /** |
388 * Returns node name. | 383 * Returns node name. |
389 */ | 384 */ |
390 devtools.profiler.Profile.DynamicCodeEntry.prototype.getName = function() { | 385 Profile.DynamicCodeEntry.prototype.getName = function() { |
391 var name = this.name; | 386 var name = this.name; |
392 if (name.length == 0) { | 387 if (name.length == 0) { |
393 name = '<anonymous>'; | 388 name = '<anonymous>'; |
394 } else if (name.charAt(0) == ' ') { | 389 } else if (name.charAt(0) == ' ') { |
395 // An anonymous function with location: " aaa.js:10". | 390 // An anonymous function with location: " aaa.js:10". |
396 name = '<anonymous>' + name; | 391 name = '<anonymous>' + name; |
397 } | 392 } |
398 return this.type + ': ' + name; | 393 return this.type + ': ' + name; |
399 }; | 394 }; |
400 | 395 |
401 | 396 |
402 /** | 397 /** |
403 * Returns raw node name (without type decoration). | 398 * Returns raw node name (without type decoration). |
404 */ | 399 */ |
405 devtools.profiler.Profile.DynamicCodeEntry.prototype.getRawName = function() { | 400 Profile.DynamicCodeEntry.prototype.getRawName = function() { |
406 return this.name; | 401 return this.name; |
407 }; | 402 }; |
408 | 403 |
409 | 404 |
410 devtools.profiler.Profile.DynamicCodeEntry.prototype.isJSFunction = function() { | 405 Profile.DynamicCodeEntry.prototype.isJSFunction = function() { |
411 return this.type == "Function" || | 406 return this.type == "Function" || |
412 this.type == "LazyCompile" || | 407 this.type == "LazyCompile" || |
413 this.type == "Script"; | 408 this.type == "Script"; |
414 }; | 409 }; |
415 | 410 |
416 | 411 |
417 /** | 412 /** |
418 * Constructs a call graph. | 413 * Constructs a call graph. |
419 * | 414 * |
420 * @constructor | 415 * @constructor |
421 */ | 416 */ |
422 devtools.profiler.CallTree = function() { | 417 function CallTree() { |
423 this.root_ = new devtools.profiler.CallTree.Node( | 418 this.root_ = new CallTree.Node( |
424 devtools.profiler.CallTree.ROOT_NODE_LABEL); | 419 CallTree.ROOT_NODE_LABEL); |
425 }; | 420 }; |
426 | 421 |
427 | 422 |
428 /** | 423 /** |
429 * The label of the root node. | 424 * The label of the root node. |
430 */ | 425 */ |
431 devtools.profiler.CallTree.ROOT_NODE_LABEL = ''; | 426 CallTree.ROOT_NODE_LABEL = ''; |
432 | 427 |
433 | 428 |
434 /** | 429 /** |
435 * @private | 430 * @private |
436 */ | 431 */ |
437 devtools.profiler.CallTree.prototype.totalsComputed_ = false; | 432 CallTree.prototype.totalsComputed_ = false; |
438 | 433 |
439 | 434 |
440 /** | 435 /** |
441 * Returns the tree root. | 436 * Returns the tree root. |
442 */ | 437 */ |
443 devtools.profiler.CallTree.prototype.getRoot = function() { | 438 CallTree.prototype.getRoot = function() { |
444 return this.root_; | 439 return this.root_; |
445 }; | 440 }; |
446 | 441 |
447 | 442 |
448 /** | 443 /** |
449 * Adds the specified call path, constructing nodes as necessary. | 444 * Adds the specified call path, constructing nodes as necessary. |
450 * | 445 * |
451 * @param {Array<string>} path Call path. | 446 * @param {Array<string>} path Call path. |
452 */ | 447 */ |
453 devtools.profiler.CallTree.prototype.addPath = function(path) { | 448 CallTree.prototype.addPath = function(path) { |
454 if (path.length == 0) { | 449 if (path.length == 0) { |
455 return; | 450 return; |
456 } | 451 } |
457 var curr = this.root_; | 452 var curr = this.root_; |
458 for (var i = 0; i < path.length; ++i) { | 453 for (var i = 0; i < path.length; ++i) { |
459 curr = curr.findOrAddChild(path[i]); | 454 curr = curr.findOrAddChild(path[i]); |
460 } | 455 } |
461 curr.selfWeight++; | 456 curr.selfWeight++; |
462 this.totalsComputed_ = false; | 457 this.totalsComputed_ = false; |
463 }; | 458 }; |
464 | 459 |
465 | 460 |
466 /** | 461 /** |
467 * Finds an immediate child of the specified parent with the specified | 462 * Finds an immediate child of the specified parent with the specified |
468 * label, creates a child node if necessary. If a parent node isn't | 463 * label, creates a child node if necessary. If a parent node isn't |
469 * specified, uses tree root. | 464 * specified, uses tree root. |
470 * | 465 * |
471 * @param {string} label Child node label. | 466 * @param {string} label Child node label. |
472 */ | 467 */ |
473 devtools.profiler.CallTree.prototype.findOrAddChild = function(label) { | 468 CallTree.prototype.findOrAddChild = function(label) { |
474 return this.root_.findOrAddChild(label); | 469 return this.root_.findOrAddChild(label); |
475 }; | 470 }; |
476 | 471 |
477 | 472 |
478 /** | 473 /** |
479 * Creates a subtree by cloning and merging all subtrees rooted at nodes | 474 * Creates a subtree by cloning and merging all subtrees rooted at nodes |
480 * with a given label. E.g. cloning the following call tree on label 'A' | 475 * with a given label. E.g. cloning the following call tree on label 'A' |
481 * will give the following result: | 476 * will give the following result: |
482 * | 477 * |
483 * <A>--<B> <B> | 478 * <A>--<B> <B> |
484 * / / | 479 * / / |
485 * <root> == clone on 'A' ==> <root>--<A> | 480 * <root> == clone on 'A' ==> <root>--<A> |
486 * \ \ | 481 * \ \ |
487 * <C>--<A>--<D> <D> | 482 * <C>--<A>--<D> <D> |
488 * | 483 * |
489 * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the | 484 * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the |
490 * source call tree. | 485 * source call tree. |
491 * | 486 * |
492 * @param {string} label The label of the new root node. | 487 * @param {string} label The label of the new root node. |
493 */ | 488 */ |
494 devtools.profiler.CallTree.prototype.cloneSubtree = function(label) { | 489 CallTree.prototype.cloneSubtree = function(label) { |
495 var subTree = new devtools.profiler.CallTree(); | 490 var subTree = new CallTree(); |
496 this.traverse(function(node, parent) { | 491 this.traverse(function(node, parent) { |
497 if (!parent && node.label != label) { | 492 if (!parent && node.label != label) { |
498 return null; | 493 return null; |
499 } | 494 } |
500 var child = (parent ? parent : subTree).findOrAddChild(node.label); | 495 var child = (parent ? parent : subTree).findOrAddChild(node.label); |
501 child.selfWeight += node.selfWeight; | 496 child.selfWeight += node.selfWeight; |
502 return child; | 497 return child; |
503 }); | 498 }); |
504 return subTree; | 499 return subTree; |
505 }; | 500 }; |
506 | 501 |
507 | 502 |
508 /** | 503 /** |
509 * Computes total weights in the call graph. | 504 * Computes total weights in the call graph. |
510 */ | 505 */ |
511 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { | 506 CallTree.prototype.computeTotalWeights = function() { |
512 if (this.totalsComputed_) { | 507 if (this.totalsComputed_) { |
513 return; | 508 return; |
514 } | 509 } |
515 this.root_.computeTotalWeight(); | 510 this.root_.computeTotalWeight(); |
516 this.totalsComputed_ = true; | 511 this.totalsComputed_ = true; |
517 }; | 512 }; |
518 | 513 |
519 | 514 |
520 /** | 515 /** |
521 * Traverses the call graph in preorder. This function can be used for | 516 * Traverses the call graph in preorder. This function can be used for |
522 * building optionally modified tree clones. This is the boilerplate code | 517 * building optionally modified tree clones. This is the boilerplate code |
523 * for this scenario: | 518 * for this scenario: |
524 * | 519 * |
525 * callTree.traverse(function(node, parentClone) { | 520 * callTree.traverse(function(node, parentClone) { |
526 * var nodeClone = cloneNode(node); | 521 * var nodeClone = cloneNode(node); |
527 * if (parentClone) | 522 * if (parentClone) |
528 * parentClone.addChild(nodeClone); | 523 * parentClone.addChild(nodeClone); |
529 * return nodeClone; | 524 * return nodeClone; |
530 * }); | 525 * }); |
531 * | 526 * |
532 * @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. | 527 * @param {function(CallTree.Node, *)} f Visitor function. |
533 * The second parameter is the result of calling 'f' on the parent node. | 528 * The second parameter is the result of calling 'f' on the parent node. |
534 */ | 529 */ |
535 devtools.profiler.CallTree.prototype.traverse = function(f) { | 530 CallTree.prototype.traverse = function(f) { |
536 var pairsToProcess = new ConsArray(); | 531 var pairsToProcess = new ConsArray(); |
537 pairsToProcess.concat([{node: this.root_, param: null}]); | 532 pairsToProcess.concat([{node: this.root_, param: null}]); |
538 while (!pairsToProcess.atEnd()) { | 533 while (!pairsToProcess.atEnd()) { |
539 var pair = pairsToProcess.next(); | 534 var pair = pairsToProcess.next(); |
540 var node = pair.node; | 535 var node = pair.node; |
541 var newParam = f(node, pair.param); | 536 var newParam = f(node, pair.param); |
542 var morePairsToProcess = []; | 537 var morePairsToProcess = []; |
543 node.forEachChild(function (child) { | 538 node.forEachChild(function (child) { |
544 morePairsToProcess.push({node: child, param: newParam}); }); | 539 morePairsToProcess.push({node: child, param: newParam}); }); |
545 pairsToProcess.concat(morePairsToProcess); | 540 pairsToProcess.concat(morePairsToProcess); |
546 } | 541 } |
547 }; | 542 }; |
548 | 543 |
549 | 544 |
550 /** | 545 /** |
551 * Performs an indepth call graph traversal. | 546 * Performs an indepth call graph traversal. |
552 * | 547 * |
553 * @param {function(devtools.profiler.CallTree.Node)} enter A function called | 548 * @param {function(CallTree.Node)} enter A function called |
554 * prior to visiting node's children. | 549 * prior to visiting node's children. |
555 * @param {function(devtools.profiler.CallTree.Node)} exit A function called | 550 * @param {function(CallTree.Node)} exit A function called |
556 * after visiting node's children. | 551 * after visiting node's children. |
557 */ | 552 */ |
558 devtools.profiler.CallTree.prototype.traverseInDepth = function(enter, exit) { | 553 CallTree.prototype.traverseInDepth = function(enter, exit) { |
559 function traverse(node) { | 554 function traverse(node) { |
560 enter(node); | 555 enter(node); |
561 node.forEachChild(traverse); | 556 node.forEachChild(traverse); |
562 exit(node); | 557 exit(node); |
563 } | 558 } |
564 traverse(this.root_); | 559 traverse(this.root_); |
565 }; | 560 }; |
566 | 561 |
567 | 562 |
568 /** | 563 /** |
569 * Constructs a call graph node. | 564 * Constructs a call graph node. |
570 * | 565 * |
571 * @param {string} label Node label. | 566 * @param {string} label Node label. |
572 * @param {devtools.profiler.CallTree.Node} opt_parent Node parent. | 567 * @param {CallTree.Node} opt_parent Node parent. |
573 */ | 568 */ |
574 devtools.profiler.CallTree.Node = function(label, opt_parent) { | 569 CallTree.Node = function(label, opt_parent) { |
575 this.label = label; | 570 this.label = label; |
576 this.parent = opt_parent; | 571 this.parent = opt_parent; |
577 this.children = {}; | 572 this.children = {}; |
578 }; | 573 }; |
579 | 574 |
580 | 575 |
581 /** | 576 /** |
582 * Node self weight (how many times this node was the last node in | 577 * Node self weight (how many times this node was the last node in |
583 * a call path). | 578 * a call path). |
584 * @type {number} | 579 * @type {number} |
585 */ | 580 */ |
586 devtools.profiler.CallTree.Node.prototype.selfWeight = 0; | 581 CallTree.Node.prototype.selfWeight = 0; |
587 | 582 |
588 | 583 |
589 /** | 584 /** |
590 * Node total weight (includes weights of all children). | 585 * Node total weight (includes weights of all children). |
591 * @type {number} | 586 * @type {number} |
592 */ | 587 */ |
593 devtools.profiler.CallTree.Node.prototype.totalWeight = 0; | 588 CallTree.Node.prototype.totalWeight = 0; |
594 | 589 |
595 | 590 |
596 /** | 591 /** |
597 * Adds a child node. | 592 * Adds a child node. |
598 * | 593 * |
599 * @param {string} label Child node label. | 594 * @param {string} label Child node label. |
600 */ | 595 */ |
601 devtools.profiler.CallTree.Node.prototype.addChild = function(label) { | 596 CallTree.Node.prototype.addChild = function(label) { |
602 var child = new devtools.profiler.CallTree.Node(label, this); | 597 var child = new CallTree.Node(label, this); |
603 this.children[label] = child; | 598 this.children[label] = child; |
604 return child; | 599 return child; |
605 }; | 600 }; |
606 | 601 |
607 | 602 |
608 /** | 603 /** |
609 * Computes node's total weight. | 604 * Computes node's total weight. |
610 */ | 605 */ |
611 devtools.profiler.CallTree.Node.prototype.computeTotalWeight = | 606 CallTree.Node.prototype.computeTotalWeight = |
612 function() { | 607 function() { |
613 var totalWeight = this.selfWeight; | 608 var totalWeight = this.selfWeight; |
614 this.forEachChild(function(child) { | 609 this.forEachChild(function(child) { |
615 totalWeight += child.computeTotalWeight(); }); | 610 totalWeight += child.computeTotalWeight(); }); |
616 return this.totalWeight = totalWeight; | 611 return this.totalWeight = totalWeight; |
617 }; | 612 }; |
618 | 613 |
619 | 614 |
620 /** | 615 /** |
621 * Returns all node's children as an array. | 616 * Returns all node's children as an array. |
622 */ | 617 */ |
623 devtools.profiler.CallTree.Node.prototype.exportChildren = function() { | 618 CallTree.Node.prototype.exportChildren = function() { |
624 var result = []; | 619 var result = []; |
625 this.forEachChild(function (node) { result.push(node); }); | 620 this.forEachChild(function (node) { result.push(node); }); |
626 return result; | 621 return result; |
627 }; | 622 }; |
628 | 623 |
629 | 624 |
630 /** | 625 /** |
631 * Finds an immediate child with the specified label. | 626 * Finds an immediate child with the specified label. |
632 * | 627 * |
633 * @param {string} label Child node label. | 628 * @param {string} label Child node label. |
634 */ | 629 */ |
635 devtools.profiler.CallTree.Node.prototype.findChild = function(label) { | 630 CallTree.Node.prototype.findChild = function(label) { |
636 return this.children[label] || null; | 631 return this.children[label] || null; |
637 }; | 632 }; |
638 | 633 |
639 | 634 |
640 /** | 635 /** |
641 * Finds an immediate child with the specified label, creates a child | 636 * Finds an immediate child with the specified label, creates a child |
642 * node if necessary. | 637 * node if necessary. |
643 * | 638 * |
644 * @param {string} label Child node label. | 639 * @param {string} label Child node label. |
645 */ | 640 */ |
646 devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(label) { | 641 CallTree.Node.prototype.findOrAddChild = function(label) { |
647 return this.findChild(label) || this.addChild(label); | 642 return this.findChild(label) || this.addChild(label); |
648 }; | 643 }; |
649 | 644 |
650 | 645 |
651 /** | 646 /** |
652 * Calls the specified function for every child. | 647 * Calls the specified function for every child. |
653 * | 648 * |
654 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 649 * @param {function(CallTree.Node)} f Visitor function. |
655 */ | 650 */ |
656 devtools.profiler.CallTree.Node.prototype.forEachChild = function(f) { | 651 CallTree.Node.prototype.forEachChild = function(f) { |
657 for (var c in this.children) { | 652 for (var c in this.children) { |
658 f(this.children[c]); | 653 f(this.children[c]); |
659 } | 654 } |
660 }; | 655 }; |
661 | 656 |
662 | 657 |
663 /** | 658 /** |
664 * Walks up from the current node up to the call tree root. | 659 * Walks up from the current node up to the call tree root. |
665 * | 660 * |
666 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 661 * @param {function(CallTree.Node)} f Visitor function. |
667 */ | 662 */ |
668 devtools.profiler.CallTree.Node.prototype.walkUpToRoot = function(f) { | 663 CallTree.Node.prototype.walkUpToRoot = function(f) { |
669 for (var curr = this; curr != null; curr = curr.parent) { | 664 for (var curr = this; curr != null; curr = curr.parent) { |
670 f(curr); | 665 f(curr); |
671 } | 666 } |
672 }; | 667 }; |
673 | 668 |
674 | 669 |
675 /** | 670 /** |
676 * Tries to find a node with the specified path. | 671 * Tries to find a node with the specified path. |
677 * | 672 * |
678 * @param {Array<string>} labels The path. | 673 * @param {Array<string>} labels The path. |
679 * @param {function(devtools.profiler.CallTree.Node)} opt_f Visitor function. | 674 * @param {function(CallTree.Node)} opt_f Visitor function. |
680 */ | 675 */ |
681 devtools.profiler.CallTree.Node.prototype.descendToChild = function( | 676 CallTree.Node.prototype.descendToChild = function( |
682 labels, opt_f) { | 677 labels, opt_f) { |
683 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { | 678 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { |
684 var child = curr.findChild(labels[pos]); | 679 var child = curr.findChild(labels[pos]); |
685 if (opt_f) { | 680 if (opt_f) { |
686 opt_f(child, pos); | 681 opt_f(child, pos); |
687 } | 682 } |
688 curr = child; | 683 curr = child; |
689 } | 684 } |
690 return curr; | 685 return curr; |
691 }; | 686 }; |
OLD | NEW |