| 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 |