| 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 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 * Should be overriden by subclasses. | 49 * Should be overriden by subclasses. |
| 50 * | 50 * |
| 51 * @param {string} name Function name. | 51 * @param {string} name Function name. |
| 52 */ | 52 */ |
| 53 devtools.profiler.Profile.prototype.skipThisFunction = function(name) { | 53 devtools.profiler.Profile.prototype.skipThisFunction = function(name) { |
| 54 return false; | 54 return false; |
| 55 }; | 55 }; |
| 56 | 56 |
| 57 | 57 |
| 58 /** | 58 /** |
| 59 * Enum for profiler operations that involve looking up existing |
| 60 * code entries. |
| 61 * |
| 62 * @enum {number} |
| 63 */ |
| 64 devtools.profiler.Profile.Operation = { |
| 65 MOVE: 0, |
| 66 DELETE: 1, |
| 67 TICK: 2 |
| 68 }; |
| 69 |
| 70 |
| 71 /** |
| 59 * Called whenever the specified operation has failed finding a function | 72 * Called whenever the specified operation has failed finding a function |
| 60 * containing the specified address. Should be overriden by subclasses. | 73 * containing the specified address. Should be overriden by subclasses. |
| 61 * Operation is one of the following: 'move', 'delete', 'tick'. | 74 * See the devtools.profiler.Profile.Operation enum for the list of |
| 75 * possible operations. |
| 62 * | 76 * |
| 63 * @param {string} operation Operation name. | 77 * @param {number} operation Operation. |
| 64 * @param {number} addr Address of the unknown code. | 78 * @param {number} addr Address of the unknown code. |
| 79 * @param {number} opt_stackPos If an unknown address is encountered |
| 80 * during stack strace processing, specifies a position of the frame |
| 81 * containing the address. |
| 65 */ | 82 */ |
| 66 devtools.profiler.Profile.prototype.handleUnknownCode = function( | 83 devtools.profiler.Profile.prototype.handleUnknownCode = function( |
| 67 operation, addr) { | 84 operation, addr, opt_stackPos) { |
| 68 }; | 85 }; |
| 69 | 86 |
| 70 | 87 |
| 71 /** | 88 /** |
| 72 * Registers static (library) code entry. | 89 * Registers static (library) code entry. |
| 73 * | 90 * |
| 74 * @param {string} name Code entry name. | 91 * @param {string} name Code entry name. |
| 75 * @param {number} startAddr Starting address. | 92 * @param {number} startAddr Starting address. |
| 76 * @param {number} endAddr Ending address. | 93 * @param {number} endAddr Ending address. |
| 77 */ | 94 */ |
| 78 devtools.profiler.Profile.prototype.addStaticCode = function( | 95 devtools.profiler.Profile.prototype.addStaticCode = function( |
| 79 name, startAddr, endAddr) { | 96 name, startAddr, endAddr) { |
| 80 this.codeMap_.addStaticCode(startAddr, | 97 var entry = new devtools.profiler.CodeMap.CodeEntry( |
| 81 new devtools.profiler.CodeMap.CodeEntry(endAddr - startAddr, name)); | 98 endAddr - startAddr, name); |
| 99 this.codeMap_.addStaticCode(startAddr, entry); |
| 100 return entry; |
| 82 }; | 101 }; |
| 83 | 102 |
| 84 | 103 |
| 85 /** | 104 /** |
| 86 * Registers dynamic (JIT-compiled) code entry. | 105 * Registers dynamic (JIT-compiled) code entry. |
| 87 * | 106 * |
| 88 * @param {string} type Code entry type. | 107 * @param {string} type Code entry type. |
| 89 * @param {string} name Code entry name. | 108 * @param {string} name Code entry name. |
| 90 * @param {number} start Starting address. | 109 * @param {number} start Starting address. |
| 91 * @param {number} size Code entry size. | 110 * @param {number} size Code entry size. |
| 92 */ | 111 */ |
| 93 devtools.profiler.Profile.prototype.addCode = function( | 112 devtools.profiler.Profile.prototype.addCode = function( |
| 94 type, name, start, size) { | 113 type, name, start, size) { |
| 95 this.codeMap_.addCode(start, | 114 var entry = new devtools.profiler.Profile.DynamicCodeEntry(size, type, name); |
| 96 new devtools.profiler.Profile.DynamicCodeEntry(size, type, name)); | 115 this.codeMap_.addCode(start, entry); |
| 116 return entry; |
| 97 }; | 117 }; |
| 98 | 118 |
| 99 | 119 |
| 100 /** | 120 /** |
| 101 * Reports about moving of a dynamic code entry. | 121 * Reports about moving of a dynamic code entry. |
| 102 * | 122 * |
| 103 * @param {number} from Current code entry address. | 123 * @param {number} from Current code entry address. |
| 104 * @param {number} to New code entry address. | 124 * @param {number} to New code entry address. |
| 105 */ | 125 */ |
| 106 devtools.profiler.Profile.prototype.moveCode = function(from, to) { | 126 devtools.profiler.Profile.prototype.moveCode = function(from, to) { |
| 107 try { | 127 try { |
| 108 this.codeMap_.moveCode(from, to); | 128 this.codeMap_.moveCode(from, to); |
| 109 } catch (e) { | 129 } catch (e) { |
| 110 this.handleUnknownCode('move', from); | 130 this.handleUnknownCode(devtools.profiler.Profile.Operation.MOVE, from); |
| 111 } | 131 } |
| 112 }; | 132 }; |
| 113 | 133 |
| 114 | 134 |
| 115 /** | 135 /** |
| 116 * Reports about deletion of a dynamic code entry. | 136 * Reports about deletion of a dynamic code entry. |
| 117 * | 137 * |
| 118 * @param {number} start Starting address. | 138 * @param {number} start Starting address. |
| 119 */ | 139 */ |
| 120 devtools.profiler.Profile.prototype.deleteCode = function(start) { | 140 devtools.profiler.Profile.prototype.deleteCode = function(start) { |
| 121 try { | 141 try { |
| 122 this.codeMap_.deleteCode(start); | 142 this.codeMap_.deleteCode(start); |
| 123 } catch (e) { | 143 } catch (e) { |
| 124 this.handleUnknownCode('delete', start); | 144 this.handleUnknownCode(devtools.profiler.Profile.Operation.DELETE, start); |
| 125 } | 145 } |
| 126 }; | 146 }; |
| 127 | 147 |
| 128 | 148 |
| 129 /** | 149 /** |
| 130 * Records a tick event. Stack must contain a sequence of | 150 * Records a tick event. Stack must contain a sequence of |
| 131 * addresses starting with the program counter value. | 151 * addresses starting with the program counter value. |
| 132 * | 152 * |
| 133 * @param {Array<number>} stack Stack sample. | 153 * @param {Array<number>} stack Stack sample. |
| 134 */ | 154 */ |
| (...skipping 14 matching lines...) Expand all Loading... |
| 149 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { | 169 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { |
| 150 var result = []; | 170 var result = []; |
| 151 for (var i = 0; i < stack.length; ++i) { | 171 for (var i = 0; i < stack.length; ++i) { |
| 152 var entry = this.codeMap_.findEntry(stack[i]); | 172 var entry = this.codeMap_.findEntry(stack[i]); |
| 153 if (entry) { | 173 if (entry) { |
| 154 var name = entry.getName(); | 174 var name = entry.getName(); |
| 155 if (!this.skipThisFunction(name)) { | 175 if (!this.skipThisFunction(name)) { |
| 156 result.push(name); | 176 result.push(name); |
| 157 } | 177 } |
| 158 } else { | 178 } else { |
| 159 this.handleUnknownCode('tick', stack[i]); | 179 this.handleUnknownCode( |
| 180 devtools.profiler.Profile.Operation.TICK, stack[i], i); |
| 160 } | 181 } |
| 161 } | 182 } |
| 162 return result; | 183 return result; |
| 163 }; | 184 }; |
| 164 | 185 |
| 165 | 186 |
| 166 /** | 187 /** |
| 167 * Returns the root of the top down call graph. | 188 * Returns the root of the top down call graph. |
| 168 */ | 189 */ |
| 169 devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() { | 190 devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() { |
| 170 this.topDownTree_.computeTotalWeights(); | 191 this.topDownTree_.computeTotalWeights(); |
| 171 return this.topDownTree_.root_; | 192 return this.topDownTree_.getRoot(); |
| 172 }; | 193 }; |
| 173 | 194 |
| 174 | 195 |
| 175 /** | 196 /** |
| 176 * Returns the root of the bottom up call graph. | 197 * Returns the root of the bottom up call graph. |
| 177 */ | 198 */ |
| 178 devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() { | 199 devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() { |
| 179 this.bottomUpTree_.computeTotalWeights(); | 200 this.bottomUpTree_.computeTotalWeights(); |
| 180 return this.bottomUpTree_.root_; | 201 return this.bottomUpTree_.getRoot(); |
| 181 }; | 202 }; |
| 182 | 203 |
| 183 | 204 |
| 184 /** | 205 /** |
| 185 * Traverses the top down call graph in preorder. | 206 * Traverses the top down call graph in preorder. |
| 186 * | 207 * |
| 187 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 208 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
| 188 */ | 209 */ |
| 189 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { | 210 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { |
| 190 this.topDownTree_.traverse(f); | 211 this.topDownTree_.traverse(f); |
| 191 }; | 212 }; |
| 192 | 213 |
| 193 | 214 |
| 194 /** | 215 /** |
| 195 * Traverses the bottom up call graph in preorder. | 216 * Traverses the bottom up call graph in preorder. |
| 196 * | 217 * |
| 197 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 218 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
| 198 */ | 219 */ |
| 199 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { | 220 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { |
| 200 this.bottomUpTree_.traverse(f); | 221 this.bottomUpTree_.traverse(f); |
| 201 }; | 222 }; |
| 202 | 223 |
| 203 | 224 |
| 204 /** | 225 /** |
| 226 * Calculates a top down profile starting from the specified node. |
| 227 * |
| 228 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
| 229 */ |
| 230 devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_root) { |
| 231 if (!opt_root) { |
| 232 this.topDownTree_.computeTotalWeights(); |
| 233 return this.topDownTree_; |
| 234 } else { |
| 235 throw Error('not implemented'); |
| 236 } |
| 237 }; |
| 238 |
| 239 |
| 240 /** |
| 241 * Calculates a bottom up profile starting from the specified node. |
| 242 * |
| 243 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
| 244 */ |
| 245 devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_root) { |
| 246 if (!opt_root) { |
| 247 this.bottomUpTree_.computeTotalWeights(); |
| 248 return this.bottomUpTree_; |
| 249 } else { |
| 250 throw Error('not implemented'); |
| 251 } |
| 252 }; |
| 253 |
| 254 |
| 255 /** |
| 205 * Calculates a flat profile of callees starting from the specified node. | 256 * Calculates a flat profile of callees starting from the specified node. |
| 206 * | 257 * |
| 207 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. | 258 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
| 208 */ | 259 */ |
| 209 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) { | 260 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) { |
| 210 var counters = new devtools.profiler.CallTree.Node(''); | 261 var counters = new devtools.profiler.CallTree(); |
| 211 var precs = {}; | 262 var precs = {}; |
| 212 this.topDownTree_.computeTotalWeights(); | 263 this.topDownTree_.computeTotalWeights(); |
| 213 this.topDownTree_.traverseInDepth( | 264 this.topDownTree_.traverseInDepth( |
| 214 function onEnter(node) { | 265 function onEnter(node) { |
| 215 if (!(node.label in precs)) { | 266 if (!(node.label in precs)) { |
| 216 precs[node.label] = 0; | 267 precs[node.label] = 0; |
| 217 } | 268 } |
| 218 var rec = counters.findOrAddChild(node.label); | 269 var rec = counters.findOrAddChild(node.label); |
| 219 rec.selfWeight += node.selfWeight; | 270 rec.selfWeight += node.selfWeight; |
| 220 if (precs[node.label] == 0) { | 271 if (precs[node.label] == 0) { |
| 221 rec.totalWeight += node.totalWeight; | 272 rec.totalWeight += node.totalWeight; |
| 222 } | 273 } |
| 223 precs[node.label]++; | 274 precs[node.label]++; |
| 224 }, | 275 }, |
| 225 function onExit(node) { | 276 function onExit(node) { |
| 226 precs[node.label]--; | 277 precs[node.label]--; |
| 227 }, | 278 }, |
| 228 opt_root); | 279 opt_root); |
| 229 return counters.exportChildren(); | 280 return counters; |
| 230 }; | 281 }; |
| 231 | 282 |
| 232 | 283 |
| 233 /** | 284 /** |
| 234 * Creates a dynamic code entry. | 285 * Creates a dynamic code entry. |
| 235 * | 286 * |
| 236 * @param {number} size Code size. | 287 * @param {number} size Code size. |
| 237 * @param {string} type Code type. | 288 * @param {string} type Code type. |
| 238 * @param {string} name Function name. | 289 * @param {string} name Function name. |
| 239 * @constructor | 290 * @constructor |
| (...skipping 29 matching lines...) Expand all Loading... |
| 269 }; | 320 }; |
| 270 | 321 |
| 271 | 322 |
| 272 /** | 323 /** |
| 273 * @private | 324 * @private |
| 274 */ | 325 */ |
| 275 devtools.profiler.CallTree.prototype.totalsComputed_ = false; | 326 devtools.profiler.CallTree.prototype.totalsComputed_ = false; |
| 276 | 327 |
| 277 | 328 |
| 278 /** | 329 /** |
| 330 * Returns the tree root. |
| 331 */ |
| 332 devtools.profiler.CallTree.prototype.getRoot = function() { |
| 333 return this.root_; |
| 334 }; |
| 335 |
| 336 |
| 337 /** |
| 279 * Adds the specified call path, constructing nodes as necessary. | 338 * Adds the specified call path, constructing nodes as necessary. |
| 280 * | 339 * |
| 281 * @param {Array<string>} path Call path. | 340 * @param {Array<string>} path Call path. |
| 282 */ | 341 */ |
| 283 devtools.profiler.CallTree.prototype.addPath = function(path) { | 342 devtools.profiler.CallTree.prototype.addPath = function(path) { |
| 284 if (path.length == 0) { | 343 if (path.length == 0) { |
| 285 return; | 344 return; |
| 286 } | 345 } |
| 287 var curr = this.root_; | 346 var curr = this.root_; |
| 288 for (var i = 0; i < path.length; ++i) { | 347 for (var i = 0; i < path.length; ++i) { |
| 289 curr = curr.findOrAddChild(path[i]); | 348 curr = curr.findOrAddChild(path[i]); |
| 290 } | 349 } |
| 291 curr.selfWeight++; | 350 curr.selfWeight++; |
| 292 this.totalsComputed_ = false; | 351 this.totalsComputed_ = false; |
| 293 }; | 352 }; |
| 294 | 353 |
| 295 | 354 |
| 296 /** | 355 /** |
| 356 * Finds an immediate child of the specified parent with the specified |
| 357 * label, creates a child node if necessary. If a parent node isn't |
| 358 * specified, uses tree root. |
| 359 * |
| 360 * @param {string} label Child node label. |
| 361 */ |
| 362 devtools.profiler.CallTree.prototype.findOrAddChild = function( |
| 363 label, opt_parent) { |
| 364 var parent = opt_parent || this.root_; |
| 365 return parent.findOrAddChild(label); |
| 366 }; |
| 367 |
| 368 |
| 369 /** |
| 297 * Computes total weights in the call graph. | 370 * Computes total weights in the call graph. |
| 298 */ | 371 */ |
| 299 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { | 372 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { |
| 300 if (this.totalsComputed_) { | 373 if (this.totalsComputed_) { |
| 301 return; | 374 return; |
| 302 } | 375 } |
| 303 this.root_.computeTotalWeight(); | 376 this.root_.computeTotalWeight(); |
| 304 this.totalsComputed_ = true; | 377 this.totalsComputed_ = true; |
| 305 }; | 378 }; |
| 306 | 379 |
| 307 | 380 |
| 308 /** | 381 /** |
| 309 * Traverses the call graph in preorder. | 382 * Traverses the call graph in preorder. This function can be used for |
| 383 * building optionally modified tree clones. This is the boilerplate code |
| 384 * for this scenario: |
| 310 * | 385 * |
| 311 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 386 * callTree.traverse(function(node, parentClone) { |
| 387 * var nodeClone = cloneNode(node); |
| 388 * if (parentClone) |
| 389 * parentClone.addChild(nodeClone); |
| 390 * return nodeClone; |
| 391 * }); |
| 392 * |
| 393 * @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. |
| 394 * The second parameter is the result of calling 'f' on the parent node. |
| 312 * @param {devtools.profiler.CallTree.Node} opt_start Starting node. | 395 * @param {devtools.profiler.CallTree.Node} opt_start Starting node. |
| 313 */ | 396 */ |
| 314 devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { | 397 devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { |
| 315 var nodesToVisit = [opt_start || this.root_]; | 398 var pairsToProcess = [{node: opt_start || this.root_, param: null}]; |
| 316 while (nodesToVisit.length > 0) { | 399 while (pairsToProcess.length > 0) { |
| 317 var node = nodesToVisit.shift(); | 400 var pair = pairsToProcess.shift(); |
| 318 f(node); | 401 var node = pair.node; |
| 319 nodesToVisit = nodesToVisit.concat(node.exportChildren()); | 402 var newParam = f(node, pair.param); |
| 403 node.forEachChild( |
| 404 function (child) { pairsToProcess.push({node: child, param: newParam}); } |
| 405 ); |
| 320 } | 406 } |
| 321 }; | 407 }; |
| 322 | 408 |
| 323 | 409 |
| 324 /** | 410 /** |
| 325 * Performs an indepth call graph traversal. | 411 * Performs an indepth call graph traversal. |
| 326 * | 412 * |
| 327 * @param {function(devtools.profiler.CallTree.Node)} enter A function called | 413 * @param {function(devtools.profiler.CallTree.Node)} enter A function called |
| 328 * prior to visiting node's children. | 414 * prior to visiting node's children. |
| 329 * @param {function(devtools.profiler.CallTree.Node)} exit A function called | 415 * @param {function(devtools.profiler.CallTree.Node)} exit A function called |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 labels, opt_f) { | 545 labels, opt_f) { |
| 460 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { | 546 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { |
| 461 var child = curr.findChild(labels[pos]); | 547 var child = curr.findChild(labels[pos]); |
| 462 if (opt_f) { | 548 if (opt_f) { |
| 463 opt_f(child, pos); | 549 opt_f(child, pos); |
| 464 } | 550 } |
| 465 curr = child; | 551 curr = child; |
| 466 } | 552 } |
| 467 return curr; | 553 return curr; |
| 468 }; | 554 }; |
| OLD | NEW |