| 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 /** | |
| 47 * Version of profiler log. | |
| 48 */ | |
| 49 devtools.profiler.Profile.VERSION = 2; | |
| 50 | |
| 51 | 41 |
| 52 /** | 42 /** |
| 53 * Returns whether a function with the specified name must be skipped. | 43 * Returns whether a function with the specified name must be skipped. |
| 54 * Should be overriden by subclasses. | 44 * Should be overriden by subclasses. |
| 55 * | 45 * |
| 56 * @param {string} name Function name. | 46 * @param {string} name Function name. |
| 57 */ | 47 */ |
| 58 devtools.profiler.Profile.prototype.skipThisFunction = function(name) { | 48 Profile.prototype.skipThisFunction = function(name) { |
| 59 return false; | 49 return false; |
| 60 }; | 50 }; |
| 61 | 51 |
| 62 | 52 |
| 63 /** | 53 /** |
| 64 * Enum for profiler operations that involve looking up existing | 54 * Enum for profiler operations that involve looking up existing |
| 65 * code entries. | 55 * code entries. |
| 66 * | 56 * |
| 67 * @enum {number} | 57 * @enum {number} |
| 68 */ | 58 */ |
| 69 devtools.profiler.Profile.Operation = { | 59 Profile.Operation = { |
| 70 MOVE: 0, | 60 MOVE: 0, |
| 71 DELETE: 1, | 61 DELETE: 1, |
| 72 TICK: 2 | 62 TICK: 2 |
| 73 }; | 63 }; |
| 74 | 64 |
| 75 | 65 |
| 76 /** | 66 /** |
| 67 * Enum for code state regarding its dynamic optimization. |
| 68 * |
| 69 * @enum {number} |
| 70 */ |
| 71 Profile.CodeState = { |
| 72 COMPILED: 0, |
| 73 OPTIMIZABLE: 1, |
| 74 OPTIMIZED: 2 |
| 75 }; |
| 76 |
| 77 |
| 78 /** |
| 77 * Called whenever the specified operation has failed finding a function | 79 * Called whenever the specified operation has failed finding a function |
| 78 * containing the specified address. Should be overriden by subclasses. | 80 * containing the specified address. Should be overriden by subclasses. |
| 79 * See the devtools.profiler.Profile.Operation enum for the list of | 81 * See the Profile.Operation enum for the list of |
| 80 * possible operations. | 82 * possible operations. |
| 81 * | 83 * |
| 82 * @param {number} operation Operation. | 84 * @param {number} operation Operation. |
| 83 * @param {number} addr Address of the unknown code. | 85 * @param {number} addr Address of the unknown code. |
| 84 * @param {number} opt_stackPos If an unknown address is encountered | 86 * @param {number} opt_stackPos If an unknown address is encountered |
| 85 * during stack strace processing, specifies a position of the frame | 87 * during stack strace processing, specifies a position of the frame |
| 86 * containing the address. | 88 * containing the address. |
| 87 */ | 89 */ |
| 88 devtools.profiler.Profile.prototype.handleUnknownCode = function( | 90 Profile.prototype.handleUnknownCode = function( |
| 89 operation, addr, opt_stackPos) { | 91 operation, addr, opt_stackPos) { |
| 90 }; | 92 }; |
| 91 | 93 |
| 92 | 94 |
| 93 /** | 95 /** |
| 94 * Registers a library. | 96 * Registers a library. |
| 95 * | 97 * |
| 96 * @param {string} name Code entry name. | 98 * @param {string} name Code entry name. |
| 97 * @param {number} startAddr Starting address. | 99 * @param {number} startAddr Starting address. |
| 98 * @param {number} endAddr Ending address. | 100 * @param {number} endAddr Ending address. |
| 99 */ | 101 */ |
| 100 devtools.profiler.Profile.prototype.addLibrary = function( | 102 Profile.prototype.addLibrary = function( |
| 101 name, startAddr, endAddr) { | 103 name, startAddr, endAddr) { |
| 102 var entry = new devtools.profiler.CodeMap.CodeEntry( | 104 var entry = new CodeMap.CodeEntry( |
| 103 endAddr - startAddr, name); | 105 endAddr - startAddr, name); |
| 104 this.codeMap_.addLibrary(startAddr, entry); | 106 this.codeMap_.addLibrary(startAddr, entry); |
| 105 return entry; | 107 return entry; |
| 106 }; | 108 }; |
| 107 | 109 |
| 108 | 110 |
| 109 /** | 111 /** |
| 110 * Registers statically compiled code entry. | 112 * Registers statically compiled code entry. |
| 111 * | 113 * |
| 112 * @param {string} name Code entry name. | 114 * @param {string} name Code entry name. |
| 113 * @param {number} startAddr Starting address. | 115 * @param {number} startAddr Starting address. |
| 114 * @param {number} endAddr Ending address. | 116 * @param {number} endAddr Ending address. |
| 115 */ | 117 */ |
| 116 devtools.profiler.Profile.prototype.addStaticCode = function( | 118 Profile.prototype.addStaticCode = function( |
| 117 name, startAddr, endAddr) { | 119 name, startAddr, endAddr) { |
| 118 var entry = new devtools.profiler.CodeMap.CodeEntry( | 120 var entry = new CodeMap.CodeEntry( |
| 119 endAddr - startAddr, name); | 121 endAddr - startAddr, name); |
| 120 this.codeMap_.addStaticCode(startAddr, entry); | 122 this.codeMap_.addStaticCode(startAddr, entry); |
| 121 return entry; | 123 return entry; |
| 122 }; | 124 }; |
| 123 | 125 |
| 124 | 126 |
| 125 /** | 127 /** |
| 126 * Registers dynamic (JIT-compiled) code entry. | 128 * Registers dynamic (JIT-compiled) code entry. |
| 127 * | 129 * |
| 128 * @param {string} type Code entry type. | 130 * @param {string} type Code entry type. |
| 129 * @param {string} name Code entry name. | 131 * @param {string} name Code entry name. |
| 130 * @param {number} start Starting address. | 132 * @param {number} start Starting address. |
| 131 * @param {number} size Code entry size. | 133 * @param {number} size Code entry size. |
| 132 */ | 134 */ |
| 133 devtools.profiler.Profile.prototype.addCode = function( | 135 Profile.prototype.addCode = function( |
| 134 type, name, start, size) { | 136 type, name, start, size) { |
| 135 var entry = new devtools.profiler.Profile.DynamicCodeEntry(size, type, name); | 137 var entry = new Profile.DynamicCodeEntry(size, type, name); |
| 136 this.codeMap_.addCode(start, entry); | 138 this.codeMap_.addCode(start, entry); |
| 137 return entry; | 139 return entry; |
| 138 }; | 140 }; |
| 139 | 141 |
| 140 | 142 |
| 141 /** | 143 /** |
| 142 * Creates an alias entry for a code entry. | 144 * Registers dynamic (JIT-compiled) code entry. |
| 143 * | 145 * |
| 144 * @param {number} aliasAddr Alias address. | 146 * @param {string} type Code entry type. |
| 145 * @param {number} addr Code entry address. | 147 * @param {string} name Code entry name. |
| 148 * @param {number} start Starting address. |
| 149 * @param {number} size Code entry size. |
| 150 * @param {number} funcAddr Shared function object address. |
| 151 * @param {Profile.CodeState} state Optimization state. |
| 146 */ | 152 */ |
| 147 devtools.profiler.Profile.prototype.addCodeAlias = function( | 153 Profile.prototype.addFuncCode = function( |
| 148 aliasAddr, addr) { | 154 type, name, start, size, funcAddr, state) { |
| 149 var entry = this.codeMap_.findDynamicEntryByStartAddress(addr); | 155 // As code and functions are in the same address space, |
| 150 if (entry) { | 156 // it is safe to put them in a single code map. |
| 151 this.codeMap_.addCode(aliasAddr, entry); | 157 var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr); |
| 158 if (!func) { |
| 159 func = new Profile.FunctionEntry(name); |
| 160 this.codeMap_.addCode(funcAddr, func); |
| 161 } else if (func.name !== name) { |
| 162 // Function object has been overwritten with a new one. |
| 163 func.name = name; |
| 152 } | 164 } |
| 165 var entry = new Profile.DynamicFuncCodeEntry(size, type, func, state); |
| 166 this.codeMap_.addCode(start, entry); |
| 167 return entry; |
| 153 }; | 168 }; |
| 154 | 169 |
| 155 | 170 |
| 156 /** | 171 /** |
| 157 * Reports about moving of a dynamic code entry. | 172 * Reports about moving of a dynamic code entry. |
| 158 * | 173 * |
| 159 * @param {number} from Current code entry address. | 174 * @param {number} from Current code entry address. |
| 160 * @param {number} to New code entry address. | 175 * @param {number} to New code entry address. |
| 161 */ | 176 */ |
| 162 devtools.profiler.Profile.prototype.moveCode = function(from, to) { | 177 Profile.prototype.moveCode = function(from, to) { |
| 163 try { | 178 try { |
| 164 this.codeMap_.moveCode(from, to); | 179 this.codeMap_.moveCode(from, to); |
| 165 } catch (e) { | 180 } catch (e) { |
| 166 this.handleUnknownCode(devtools.profiler.Profile.Operation.MOVE, from); | 181 this.handleUnknownCode(Profile.Operation.MOVE, from); |
| 167 } | 182 } |
| 168 }; | 183 }; |
| 169 | 184 |
| 170 | 185 |
| 171 /** | 186 /** |
| 172 * Reports about deletion of a dynamic code entry. | 187 * Reports about deletion of a dynamic code entry. |
| 173 * | 188 * |
| 174 * @param {number} start Starting address. | 189 * @param {number} start Starting address. |
| 175 */ | 190 */ |
| 176 devtools.profiler.Profile.prototype.deleteCode = function(start) { | 191 Profile.prototype.deleteCode = function(start) { |
| 177 try { | 192 try { |
| 178 this.codeMap_.deleteCode(start); | 193 this.codeMap_.deleteCode(start); |
| 179 } catch (e) { | 194 } catch (e) { |
| 180 this.handleUnknownCode(devtools.profiler.Profile.Operation.DELETE, start); | 195 this.handleUnknownCode(Profile.Operation.DELETE, start); |
| 181 } | 196 } |
| 182 }; | 197 }; |
| 183 | 198 |
| 184 | 199 |
| 185 /** | 200 /** |
| 186 * Reports about moving of a dynamic code entry. | 201 * Reports about moving of a dynamic code entry. |
| 187 * | 202 * |
| 188 * @param {number} from Current code entry address. | 203 * @param {number} from Current code entry address. |
| 189 * @param {number} to New code entry address. | 204 * @param {number} to New code entry address. |
| 190 */ | 205 */ |
| 191 devtools.profiler.Profile.prototype.safeMoveDynamicCode = function(from, to) { | 206 Profile.prototype.moveFunc = function(from, to) { |
| 192 if (this.codeMap_.findDynamicEntryByStartAddress(from)) { | 207 if (this.codeMap_.findDynamicEntryByStartAddress(from)) { |
| 193 this.codeMap_.moveCode(from, to); | 208 this.codeMap_.moveCode(from, to); |
| 194 } | 209 } |
| 195 }; | 210 }; |
| 196 | 211 |
| 197 | 212 |
| 198 /** | 213 /** |
| 199 * Reports about deletion of a dynamic code entry. | |
| 200 * | |
| 201 * @param {number} start Starting address. | |
| 202 */ | |
| 203 devtools.profiler.Profile.prototype.safeDeleteDynamicCode = function(start) { | |
| 204 if (this.codeMap_.findDynamicEntryByStartAddress(start)) { | |
| 205 this.codeMap_.deleteCode(start); | |
| 206 } | |
| 207 }; | |
| 208 | |
| 209 | |
| 210 /** | |
| 211 * Retrieves a code entry by an address. | 214 * Retrieves a code entry by an address. |
| 212 * | 215 * |
| 213 * @param {number} addr Entry address. | 216 * @param {number} addr Entry address. |
| 214 */ | 217 */ |
| 215 devtools.profiler.Profile.prototype.findEntry = function(addr) { | 218 Profile.prototype.findEntry = function(addr) { |
| 216 return this.codeMap_.findEntry(addr); | 219 return this.codeMap_.findEntry(addr); |
| 217 }; | 220 }; |
| 218 | 221 |
| 219 | 222 |
| 220 /** | 223 /** |
| 221 * Records a tick event. Stack must contain a sequence of | 224 * Records a tick event. Stack must contain a sequence of |
| 222 * addresses starting with the program counter value. | 225 * addresses starting with the program counter value. |
| 223 * | 226 * |
| 224 * @param {Array<number>} stack Stack sample. | 227 * @param {Array<number>} stack Stack sample. |
| 225 */ | 228 */ |
| 226 devtools.profiler.Profile.prototype.recordTick = function(stack) { | 229 Profile.prototype.recordTick = function(stack) { |
| 227 var processedStack = this.resolveAndFilterFuncs_(stack); | 230 var processedStack = this.resolveAndFilterFuncs_(stack); |
| 228 this.bottomUpTree_.addPath(processedStack); | 231 this.bottomUpTree_.addPath(processedStack); |
| 229 processedStack.reverse(); | 232 processedStack.reverse(); |
| 230 this.topDownTree_.addPath(processedStack); | 233 this.topDownTree_.addPath(processedStack); |
| 231 }; | 234 }; |
| 232 | 235 |
| 233 | 236 |
| 234 /** | 237 /** |
| 235 * Translates addresses into function names and filters unneeded | 238 * Translates addresses into function names and filters unneeded |
| 236 * functions. | 239 * functions. |
| 237 * | 240 * |
| 238 * @param {Array<number>} stack Stack sample. | 241 * @param {Array<number>} stack Stack sample. |
| 239 */ | 242 */ |
| 240 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { | 243 Profile.prototype.resolveAndFilterFuncs_ = function(stack) { |
| 241 var result = []; | 244 var result = []; |
| 242 for (var i = 0; i < stack.length; ++i) { | 245 for (var i = 0; i < stack.length; ++i) { |
| 243 var entry = this.codeMap_.findEntry(stack[i]); | 246 var entry = this.codeMap_.findEntry(stack[i]); |
| 244 if (entry) { | 247 if (entry) { |
| 245 var name = entry.getName(); | 248 var name = entry.getName(); |
| 246 if (!this.skipThisFunction(name)) { | 249 if (!this.skipThisFunction(name)) { |
| 247 result.push(name); | 250 result.push(name); |
| 248 } | 251 } |
| 249 } else { | 252 } else { |
| 250 this.handleUnknownCode( | 253 this.handleUnknownCode( |
| 251 devtools.profiler.Profile.Operation.TICK, stack[i], i); | 254 Profile.Operation.TICK, stack[i], i); |
| 252 } | 255 } |
| 253 } | 256 } |
| 254 return result; | 257 return result; |
| 255 }; | 258 }; |
| 256 | 259 |
| 257 | 260 |
| 258 /** | 261 /** |
| 259 * Performs a BF traversal of the top down call graph. | 262 * Performs a BF traversal of the top down call graph. |
| 260 * | 263 * |
| 261 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 264 * @param {function(CallTree.Node)} f Visitor function. |
| 262 */ | 265 */ |
| 263 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { | 266 Profile.prototype.traverseTopDownTree = function(f) { |
| 264 this.topDownTree_.traverse(f); | 267 this.topDownTree_.traverse(f); |
| 265 }; | 268 }; |
| 266 | 269 |
| 267 | 270 |
| 268 /** | 271 /** |
| 269 * Performs a BF traversal of the bottom up call graph. | 272 * Performs a BF traversal of the bottom up call graph. |
| 270 * | 273 * |
| 271 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 274 * @param {function(CallTree.Node)} f Visitor function. |
| 272 */ | 275 */ |
| 273 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { | 276 Profile.prototype.traverseBottomUpTree = function(f) { |
| 274 this.bottomUpTree_.traverse(f); | 277 this.bottomUpTree_.traverse(f); |
| 275 }; | 278 }; |
| 276 | 279 |
| 277 | 280 |
| 278 /** | 281 /** |
| 279 * Calculates a top down profile for a node with the specified label. | 282 * Calculates a top down profile for a node with the specified label. |
| 280 * If no name specified, returns the whole top down calls tree. | 283 * If no name specified, returns the whole top down calls tree. |
| 281 * | 284 * |
| 282 * @param {string} opt_label Node label. | 285 * @param {string} opt_label Node label. |
| 283 */ | 286 */ |
| 284 devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_label) { | 287 Profile.prototype.getTopDownProfile = function(opt_label) { |
| 285 return this.getTreeProfile_(this.topDownTree_, opt_label); | 288 return this.getTreeProfile_(this.topDownTree_, opt_label); |
| 286 }; | 289 }; |
| 287 | 290 |
| 288 | 291 |
| 289 /** | 292 /** |
| 290 * Calculates a bottom up profile for a node with the specified label. | 293 * Calculates a bottom up profile for a node with the specified label. |
| 291 * If no name specified, returns the whole bottom up calls tree. | 294 * If no name specified, returns the whole bottom up calls tree. |
| 292 * | 295 * |
| 293 * @param {string} opt_label Node label. | 296 * @param {string} opt_label Node label. |
| 294 */ | 297 */ |
| 295 devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_label) { | 298 Profile.prototype.getBottomUpProfile = function(opt_label) { |
| 296 return this.getTreeProfile_(this.bottomUpTree_, opt_label); | 299 return this.getTreeProfile_(this.bottomUpTree_, opt_label); |
| 297 }; | 300 }; |
| 298 | 301 |
| 299 | 302 |
| 300 /** | 303 /** |
| 301 * Helper function for calculating a tree profile. | 304 * Helper function for calculating a tree profile. |
| 302 * | 305 * |
| 303 * @param {devtools.profiler.Profile.CallTree} tree Call tree. | 306 * @param {Profile.CallTree} tree Call tree. |
| 304 * @param {string} opt_label Node label. | 307 * @param {string} opt_label Node label. |
| 305 */ | 308 */ |
| 306 devtools.profiler.Profile.prototype.getTreeProfile_ = function(tree, opt_label)
{ | 309 Profile.prototype.getTreeProfile_ = function(tree, opt_label) { |
| 307 if (!opt_label) { | 310 if (!opt_label) { |
| 308 tree.computeTotalWeights(); | 311 tree.computeTotalWeights(); |
| 309 return tree; | 312 return tree; |
| 310 } else { | 313 } else { |
| 311 var subTree = tree.cloneSubtree(opt_label); | 314 var subTree = tree.cloneSubtree(opt_label); |
| 312 subTree.computeTotalWeights(); | 315 subTree.computeTotalWeights(); |
| 313 return subTree; | 316 return subTree; |
| 314 } | 317 } |
| 315 }; | 318 }; |
| 316 | 319 |
| 317 | 320 |
| 318 /** | 321 /** |
| 319 * Calculates a flat profile of callees starting from a node with | 322 * Calculates a flat profile of callees starting from a node with |
| 320 * the specified label. If no name specified, starts from the root. | 323 * the specified label. If no name specified, starts from the root. |
| 321 * | 324 * |
| 322 * @param {string} opt_label Starting node label. | 325 * @param {string} opt_label Starting node label. |
| 323 */ | 326 */ |
| 324 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_label) { | 327 Profile.prototype.getFlatProfile = function(opt_label) { |
| 325 var counters = new devtools.profiler.CallTree(); | 328 var counters = new CallTree(); |
| 326 var rootLabel = opt_label || devtools.profiler.CallTree.ROOT_NODE_LABEL; | 329 var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL; |
| 327 var precs = {}; | 330 var precs = {}; |
| 328 precs[rootLabel] = 0; | 331 precs[rootLabel] = 0; |
| 329 var root = counters.findOrAddChild(rootLabel); | 332 var root = counters.findOrAddChild(rootLabel); |
| 330 | 333 |
| 331 this.topDownTree_.computeTotalWeights(); | 334 this.topDownTree_.computeTotalWeights(); |
| 332 this.topDownTree_.traverseInDepth( | 335 this.topDownTree_.traverseInDepth( |
| 333 function onEnter(node) { | 336 function onEnter(node) { |
| 334 if (!(node.label in precs)) { | 337 if (!(node.label in precs)) { |
| 335 precs[node.label] = 0; | 338 precs[node.label] = 0; |
| 336 } | 339 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 | 374 |
| 372 | 375 |
| 373 /** | 376 /** |
| 374 * Creates a dynamic code entry. | 377 * Creates a dynamic code entry. |
| 375 * | 378 * |
| 376 * @param {number} size Code size. | 379 * @param {number} size Code size. |
| 377 * @param {string} type Code type. | 380 * @param {string} type Code type. |
| 378 * @param {string} name Function name. | 381 * @param {string} name Function name. |
| 379 * @constructor | 382 * @constructor |
| 380 */ | 383 */ |
| 381 devtools.profiler.Profile.DynamicCodeEntry = function(size, type, name) { | 384 Profile.DynamicCodeEntry = function(size, type, name) { |
| 382 devtools.profiler.CodeMap.CodeEntry.call(this, size, name); | 385 CodeMap.CodeEntry.call(this, size, name); |
| 383 this.type = type; | 386 this.type = type; |
| 384 }; | 387 }; |
| 385 | 388 |
| 386 | 389 |
| 387 /** | 390 /** |
| 388 * Returns node name. | 391 * Returns node name. |
| 389 */ | 392 */ |
| 390 devtools.profiler.Profile.DynamicCodeEntry.prototype.getName = function() { | 393 Profile.DynamicCodeEntry.prototype.getName = function() { |
| 394 return this.type + ': ' + this.name; |
| 395 }; |
| 396 |
| 397 |
| 398 /** |
| 399 * Returns raw node name (without type decoration). |
| 400 */ |
| 401 Profile.DynamicCodeEntry.prototype.getRawName = function() { |
| 402 return this.name; |
| 403 }; |
| 404 |
| 405 |
| 406 Profile.DynamicCodeEntry.prototype.isJSFunction = function() { |
| 407 return false; |
| 408 }; |
| 409 |
| 410 |
| 411 /** |
| 412 * Creates a dynamic code entry. |
| 413 * |
| 414 * @param {number} size Code size. |
| 415 * @param {string} type Code type. |
| 416 * @param {Profile.FunctionEntry} func Shared function entry. |
| 417 * @param {Profile.CodeState} state Code optimization state. |
| 418 * @constructor |
| 419 */ |
| 420 Profile.DynamicFuncCodeEntry = function(size, type, func, state) { |
| 421 CodeMap.CodeEntry.call(this, size); |
| 422 this.type = type; |
| 423 this.func = func; |
| 424 this.state = state; |
| 425 }; |
| 426 |
| 427 Profile.DynamicFuncCodeEntry.STATE_PREFIX = ["", "~", "*"]; |
| 428 |
| 429 /** |
| 430 * Returns node name. |
| 431 */ |
| 432 Profile.DynamicFuncCodeEntry.prototype.getName = function() { |
| 433 var name = this.func.getName(); |
| 434 return this.type + ': ' + Profile.DynamicFuncCodeEntry.STATE_PREFIX[this.state
] + name; |
| 435 }; |
| 436 |
| 437 |
| 438 /** |
| 439 * Returns raw node name (without type decoration). |
| 440 */ |
| 441 Profile.DynamicFuncCodeEntry.prototype.getRawName = function() { |
| 442 return this.func.getName(); |
| 443 }; |
| 444 |
| 445 |
| 446 Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function() { |
| 447 return true; |
| 448 }; |
| 449 |
| 450 |
| 451 /** |
| 452 * Creates a shared function object entry. |
| 453 * |
| 454 * @param {string} name Function name. |
| 455 * @constructor |
| 456 */ |
| 457 Profile.FunctionEntry = function(name) { |
| 458 CodeMap.CodeEntry.call(this, 0, name); |
| 459 }; |
| 460 |
| 461 |
| 462 /** |
| 463 * Returns node name. |
| 464 */ |
| 465 Profile.FunctionEntry.prototype.getName = function() { |
| 391 var name = this.name; | 466 var name = this.name; |
| 392 if (name.length == 0) { | 467 if (name.length == 0) { |
| 393 name = '<anonymous>'; | 468 name = '<anonymous>'; |
| 394 } else if (name.charAt(0) == ' ') { | 469 } else if (name.charAt(0) == ' ') { |
| 395 // An anonymous function with location: " aaa.js:10". | 470 // An anonymous function with location: " aaa.js:10". |
| 396 name = '<anonymous>' + name; | 471 name = '<anonymous>' + name; |
| 397 } | 472 } |
| 398 return this.type + ': ' + name; | 473 return name; |
| 399 }; | |
| 400 | |
| 401 | |
| 402 /** | |
| 403 * Returns raw node name (without type decoration). | |
| 404 */ | |
| 405 devtools.profiler.Profile.DynamicCodeEntry.prototype.getRawName = function() { | |
| 406 return this.name; | |
| 407 }; | |
| 408 | |
| 409 | |
| 410 devtools.profiler.Profile.DynamicCodeEntry.prototype.isJSFunction = function() { | |
| 411 return this.type == "Function" || | |
| 412 this.type == "LazyCompile" || | |
| 413 this.type == "Script"; | |
| 414 }; | 474 }; |
| 415 | 475 |
| 416 | 476 |
| 417 /** | 477 /** |
| 418 * Constructs a call graph. | 478 * Constructs a call graph. |
| 419 * | 479 * |
| 420 * @constructor | 480 * @constructor |
| 421 */ | 481 */ |
| 422 devtools.profiler.CallTree = function() { | 482 function CallTree() { |
| 423 this.root_ = new devtools.profiler.CallTree.Node( | 483 this.root_ = new CallTree.Node( |
| 424 devtools.profiler.CallTree.ROOT_NODE_LABEL); | 484 CallTree.ROOT_NODE_LABEL); |
| 425 }; | 485 }; |
| 426 | 486 |
| 427 | 487 |
| 428 /** | 488 /** |
| 429 * The label of the root node. | 489 * The label of the root node. |
| 430 */ | 490 */ |
| 431 devtools.profiler.CallTree.ROOT_NODE_LABEL = ''; | 491 CallTree.ROOT_NODE_LABEL = ''; |
| 432 | 492 |
| 433 | 493 |
| 434 /** | 494 /** |
| 435 * @private | 495 * @private |
| 436 */ | 496 */ |
| 437 devtools.profiler.CallTree.prototype.totalsComputed_ = false; | 497 CallTree.prototype.totalsComputed_ = false; |
| 438 | 498 |
| 439 | 499 |
| 440 /** | 500 /** |
| 441 * Returns the tree root. | 501 * Returns the tree root. |
| 442 */ | 502 */ |
| 443 devtools.profiler.CallTree.prototype.getRoot = function() { | 503 CallTree.prototype.getRoot = function() { |
| 444 return this.root_; | 504 return this.root_; |
| 445 }; | 505 }; |
| 446 | 506 |
| 447 | 507 |
| 448 /** | 508 /** |
| 449 * Adds the specified call path, constructing nodes as necessary. | 509 * Adds the specified call path, constructing nodes as necessary. |
| 450 * | 510 * |
| 451 * @param {Array<string>} path Call path. | 511 * @param {Array<string>} path Call path. |
| 452 */ | 512 */ |
| 453 devtools.profiler.CallTree.prototype.addPath = function(path) { | 513 CallTree.prototype.addPath = function(path) { |
| 454 if (path.length == 0) { | 514 if (path.length == 0) { |
| 455 return; | 515 return; |
| 456 } | 516 } |
| 457 var curr = this.root_; | 517 var curr = this.root_; |
| 458 for (var i = 0; i < path.length; ++i) { | 518 for (var i = 0; i < path.length; ++i) { |
| 459 curr = curr.findOrAddChild(path[i]); | 519 curr = curr.findOrAddChild(path[i]); |
| 460 } | 520 } |
| 461 curr.selfWeight++; | 521 curr.selfWeight++; |
| 462 this.totalsComputed_ = false; | 522 this.totalsComputed_ = false; |
| 463 }; | 523 }; |
| 464 | 524 |
| 465 | 525 |
| 466 /** | 526 /** |
| 467 * Finds an immediate child of the specified parent with the specified | 527 * 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 | 528 * label, creates a child node if necessary. If a parent node isn't |
| 469 * specified, uses tree root. | 529 * specified, uses tree root. |
| 470 * | 530 * |
| 471 * @param {string} label Child node label. | 531 * @param {string} label Child node label. |
| 472 */ | 532 */ |
| 473 devtools.profiler.CallTree.prototype.findOrAddChild = function(label) { | 533 CallTree.prototype.findOrAddChild = function(label) { |
| 474 return this.root_.findOrAddChild(label); | 534 return this.root_.findOrAddChild(label); |
| 475 }; | 535 }; |
| 476 | 536 |
| 477 | 537 |
| 478 /** | 538 /** |
| 479 * Creates a subtree by cloning and merging all subtrees rooted at nodes | 539 * 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' | 540 * with a given label. E.g. cloning the following call tree on label 'A' |
| 481 * will give the following result: | 541 * will give the following result: |
| 482 * | 542 * |
| 483 * <A>--<B> <B> | 543 * <A>--<B> <B> |
| 484 * / / | 544 * / / |
| 485 * <root> == clone on 'A' ==> <root>--<A> | 545 * <root> == clone on 'A' ==> <root>--<A> |
| 486 * \ \ | 546 * \ \ |
| 487 * <C>--<A>--<D> <D> | 547 * <C>--<A>--<D> <D> |
| 488 * | 548 * |
| 489 * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the | 549 * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the |
| 490 * source call tree. | 550 * source call tree. |
| 491 * | 551 * |
| 492 * @param {string} label The label of the new root node. | 552 * @param {string} label The label of the new root node. |
| 493 */ | 553 */ |
| 494 devtools.profiler.CallTree.prototype.cloneSubtree = function(label) { | 554 CallTree.prototype.cloneSubtree = function(label) { |
| 495 var subTree = new devtools.profiler.CallTree(); | 555 var subTree = new CallTree(); |
| 496 this.traverse(function(node, parent) { | 556 this.traverse(function(node, parent) { |
| 497 if (!parent && node.label != label) { | 557 if (!parent && node.label != label) { |
| 498 return null; | 558 return null; |
| 499 } | 559 } |
| 500 var child = (parent ? parent : subTree).findOrAddChild(node.label); | 560 var child = (parent ? parent : subTree).findOrAddChild(node.label); |
| 501 child.selfWeight += node.selfWeight; | 561 child.selfWeight += node.selfWeight; |
| 502 return child; | 562 return child; |
| 503 }); | 563 }); |
| 504 return subTree; | 564 return subTree; |
| 505 }; | 565 }; |
| 506 | 566 |
| 507 | 567 |
| 508 /** | 568 /** |
| 509 * Computes total weights in the call graph. | 569 * Computes total weights in the call graph. |
| 510 */ | 570 */ |
| 511 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { | 571 CallTree.prototype.computeTotalWeights = function() { |
| 512 if (this.totalsComputed_) { | 572 if (this.totalsComputed_) { |
| 513 return; | 573 return; |
| 514 } | 574 } |
| 515 this.root_.computeTotalWeight(); | 575 this.root_.computeTotalWeight(); |
| 516 this.totalsComputed_ = true; | 576 this.totalsComputed_ = true; |
| 517 }; | 577 }; |
| 518 | 578 |
| 519 | 579 |
| 520 /** | 580 /** |
| 521 * Traverses the call graph in preorder. This function can be used for | 581 * Traverses the call graph in preorder. This function can be used for |
| 522 * building optionally modified tree clones. This is the boilerplate code | 582 * building optionally modified tree clones. This is the boilerplate code |
| 523 * for this scenario: | 583 * for this scenario: |
| 524 * | 584 * |
| 525 * callTree.traverse(function(node, parentClone) { | 585 * callTree.traverse(function(node, parentClone) { |
| 526 * var nodeClone = cloneNode(node); | 586 * var nodeClone = cloneNode(node); |
| 527 * if (parentClone) | 587 * if (parentClone) |
| 528 * parentClone.addChild(nodeClone); | 588 * parentClone.addChild(nodeClone); |
| 529 * return nodeClone; | 589 * return nodeClone; |
| 530 * }); | 590 * }); |
| 531 * | 591 * |
| 532 * @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. | 592 * @param {function(CallTree.Node, *)} f Visitor function. |
| 533 * The second parameter is the result of calling 'f' on the parent node. | 593 * The second parameter is the result of calling 'f' on the parent node. |
| 534 */ | 594 */ |
| 535 devtools.profiler.CallTree.prototype.traverse = function(f) { | 595 CallTree.prototype.traverse = function(f) { |
| 536 var pairsToProcess = new ConsArray(); | 596 var pairsToProcess = new ConsArray(); |
| 537 pairsToProcess.concat([{node: this.root_, param: null}]); | 597 pairsToProcess.concat([{node: this.root_, param: null}]); |
| 538 while (!pairsToProcess.atEnd()) { | 598 while (!pairsToProcess.atEnd()) { |
| 539 var pair = pairsToProcess.next(); | 599 var pair = pairsToProcess.next(); |
| 540 var node = pair.node; | 600 var node = pair.node; |
| 541 var newParam = f(node, pair.param); | 601 var newParam = f(node, pair.param); |
| 542 var morePairsToProcess = []; | 602 var morePairsToProcess = []; |
| 543 node.forEachChild(function (child) { | 603 node.forEachChild(function (child) { |
| 544 morePairsToProcess.push({node: child, param: newParam}); }); | 604 morePairsToProcess.push({node: child, param: newParam}); }); |
| 545 pairsToProcess.concat(morePairsToProcess); | 605 pairsToProcess.concat(morePairsToProcess); |
| 546 } | 606 } |
| 547 }; | 607 }; |
| 548 | 608 |
| 549 | 609 |
| 550 /** | 610 /** |
| 551 * Performs an indepth call graph traversal. | 611 * Performs an indepth call graph traversal. |
| 552 * | 612 * |
| 553 * @param {function(devtools.profiler.CallTree.Node)} enter A function called | 613 * @param {function(CallTree.Node)} enter A function called |
| 554 * prior to visiting node's children. | 614 * prior to visiting node's children. |
| 555 * @param {function(devtools.profiler.CallTree.Node)} exit A function called | 615 * @param {function(CallTree.Node)} exit A function called |
| 556 * after visiting node's children. | 616 * after visiting node's children. |
| 557 */ | 617 */ |
| 558 devtools.profiler.CallTree.prototype.traverseInDepth = function(enter, exit) { | 618 CallTree.prototype.traverseInDepth = function(enter, exit) { |
| 559 function traverse(node) { | 619 function traverse(node) { |
| 560 enter(node); | 620 enter(node); |
| 561 node.forEachChild(traverse); | 621 node.forEachChild(traverse); |
| 562 exit(node); | 622 exit(node); |
| 563 } | 623 } |
| 564 traverse(this.root_); | 624 traverse(this.root_); |
| 565 }; | 625 }; |
| 566 | 626 |
| 567 | 627 |
| 568 /** | 628 /** |
| 569 * Constructs a call graph node. | 629 * Constructs a call graph node. |
| 570 * | 630 * |
| 571 * @param {string} label Node label. | 631 * @param {string} label Node label. |
| 572 * @param {devtools.profiler.CallTree.Node} opt_parent Node parent. | 632 * @param {CallTree.Node} opt_parent Node parent. |
| 573 */ | 633 */ |
| 574 devtools.profiler.CallTree.Node = function(label, opt_parent) { | 634 CallTree.Node = function(label, opt_parent) { |
| 575 this.label = label; | 635 this.label = label; |
| 576 this.parent = opt_parent; | 636 this.parent = opt_parent; |
| 577 this.children = {}; | 637 this.children = {}; |
| 578 }; | 638 }; |
| 579 | 639 |
| 580 | 640 |
| 581 /** | 641 /** |
| 582 * Node self weight (how many times this node was the last node in | 642 * Node self weight (how many times this node was the last node in |
| 583 * a call path). | 643 * a call path). |
| 584 * @type {number} | 644 * @type {number} |
| 585 */ | 645 */ |
| 586 devtools.profiler.CallTree.Node.prototype.selfWeight = 0; | 646 CallTree.Node.prototype.selfWeight = 0; |
| 587 | 647 |
| 588 | 648 |
| 589 /** | 649 /** |
| 590 * Node total weight (includes weights of all children). | 650 * Node total weight (includes weights of all children). |
| 591 * @type {number} | 651 * @type {number} |
| 592 */ | 652 */ |
| 593 devtools.profiler.CallTree.Node.prototype.totalWeight = 0; | 653 CallTree.Node.prototype.totalWeight = 0; |
| 594 | 654 |
| 595 | 655 |
| 596 /** | 656 /** |
| 597 * Adds a child node. | 657 * Adds a child node. |
| 598 * | 658 * |
| 599 * @param {string} label Child node label. | 659 * @param {string} label Child node label. |
| 600 */ | 660 */ |
| 601 devtools.profiler.CallTree.Node.prototype.addChild = function(label) { | 661 CallTree.Node.prototype.addChild = function(label) { |
| 602 var child = new devtools.profiler.CallTree.Node(label, this); | 662 var child = new CallTree.Node(label, this); |
| 603 this.children[label] = child; | 663 this.children[label] = child; |
| 604 return child; | 664 return child; |
| 605 }; | 665 }; |
| 606 | 666 |
| 607 | 667 |
| 608 /** | 668 /** |
| 609 * Computes node's total weight. | 669 * Computes node's total weight. |
| 610 */ | 670 */ |
| 611 devtools.profiler.CallTree.Node.prototype.computeTotalWeight = | 671 CallTree.Node.prototype.computeTotalWeight = |
| 612 function() { | 672 function() { |
| 613 var totalWeight = this.selfWeight; | 673 var totalWeight = this.selfWeight; |
| 614 this.forEachChild(function(child) { | 674 this.forEachChild(function(child) { |
| 615 totalWeight += child.computeTotalWeight(); }); | 675 totalWeight += child.computeTotalWeight(); }); |
| 616 return this.totalWeight = totalWeight; | 676 return this.totalWeight = totalWeight; |
| 617 }; | 677 }; |
| 618 | 678 |
| 619 | 679 |
| 620 /** | 680 /** |
| 621 * Returns all node's children as an array. | 681 * Returns all node's children as an array. |
| 622 */ | 682 */ |
| 623 devtools.profiler.CallTree.Node.prototype.exportChildren = function() { | 683 CallTree.Node.prototype.exportChildren = function() { |
| 624 var result = []; | 684 var result = []; |
| 625 this.forEachChild(function (node) { result.push(node); }); | 685 this.forEachChild(function (node) { result.push(node); }); |
| 626 return result; | 686 return result; |
| 627 }; | 687 }; |
| 628 | 688 |
| 629 | 689 |
| 630 /** | 690 /** |
| 631 * Finds an immediate child with the specified label. | 691 * Finds an immediate child with the specified label. |
| 632 * | 692 * |
| 633 * @param {string} label Child node label. | 693 * @param {string} label Child node label. |
| 634 */ | 694 */ |
| 635 devtools.profiler.CallTree.Node.prototype.findChild = function(label) { | 695 CallTree.Node.prototype.findChild = function(label) { |
| 636 return this.children[label] || null; | 696 return this.children[label] || null; |
| 637 }; | 697 }; |
| 638 | 698 |
| 639 | 699 |
| 640 /** | 700 /** |
| 641 * Finds an immediate child with the specified label, creates a child | 701 * Finds an immediate child with the specified label, creates a child |
| 642 * node if necessary. | 702 * node if necessary. |
| 643 * | 703 * |
| 644 * @param {string} label Child node label. | 704 * @param {string} label Child node label. |
| 645 */ | 705 */ |
| 646 devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(label) { | 706 CallTree.Node.prototype.findOrAddChild = function(label) { |
| 647 return this.findChild(label) || this.addChild(label); | 707 return this.findChild(label) || this.addChild(label); |
| 648 }; | 708 }; |
| 649 | 709 |
| 650 | 710 |
| 651 /** | 711 /** |
| 652 * Calls the specified function for every child. | 712 * Calls the specified function for every child. |
| 653 * | 713 * |
| 654 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 714 * @param {function(CallTree.Node)} f Visitor function. |
| 655 */ | 715 */ |
| 656 devtools.profiler.CallTree.Node.prototype.forEachChild = function(f) { | 716 CallTree.Node.prototype.forEachChild = function(f) { |
| 657 for (var c in this.children) { | 717 for (var c in this.children) { |
| 658 f(this.children[c]); | 718 f(this.children[c]); |
| 659 } | 719 } |
| 660 }; | 720 }; |
| 661 | 721 |
| 662 | 722 |
| 663 /** | 723 /** |
| 664 * Walks up from the current node up to the call tree root. | 724 * Walks up from the current node up to the call tree root. |
| 665 * | 725 * |
| 666 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 726 * @param {function(CallTree.Node)} f Visitor function. |
| 667 */ | 727 */ |
| 668 devtools.profiler.CallTree.Node.prototype.walkUpToRoot = function(f) { | 728 CallTree.Node.prototype.walkUpToRoot = function(f) { |
| 669 for (var curr = this; curr != null; curr = curr.parent) { | 729 for (var curr = this; curr != null; curr = curr.parent) { |
| 670 f(curr); | 730 f(curr); |
| 671 } | 731 } |
| 672 }; | 732 }; |
| 673 | 733 |
| 674 | 734 |
| 675 /** | 735 /** |
| 676 * Tries to find a node with the specified path. | 736 * Tries to find a node with the specified path. |
| 677 * | 737 * |
| 678 * @param {Array<string>} labels The path. | 738 * @param {Array<string>} labels The path. |
| 679 * @param {function(devtools.profiler.CallTree.Node)} opt_f Visitor function. | 739 * @param {function(CallTree.Node)} opt_f Visitor function. |
| 680 */ | 740 */ |
| 681 devtools.profiler.CallTree.Node.prototype.descendToChild = function( | 741 CallTree.Node.prototype.descendToChild = function( |
| 682 labels, opt_f) { | 742 labels, opt_f) { |
| 683 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { | 743 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { |
| 684 var child = curr.findChild(labels[pos]); | 744 var child = curr.findChild(labels[pos]); |
| 685 if (opt_f) { | 745 if (opt_f) { |
| 686 opt_f(child, pos); | 746 opt_f(child, pos); |
| 687 } | 747 } |
| 688 curr = child; | 748 curr = child; |
| 689 } | 749 } |
| 690 return curr; | 750 return curr; |
| 691 }; | 751 }; |
| OLD | NEW |