| 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 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 } else { | 178 } else { |
| 179 this.handleUnknownCode( | 179 this.handleUnknownCode( |
| 180 devtools.profiler.Profile.Operation.TICK, stack[i], i); | 180 devtools.profiler.Profile.Operation.TICK, stack[i], i); |
| 181 } | 181 } |
| 182 } | 182 } |
| 183 return result; | 183 return result; |
| 184 }; | 184 }; |
| 185 | 185 |
| 186 | 186 |
| 187 /** | 187 /** |
| 188 * Returns the root of the top down call graph. | 188 * Performs a BF traversal of the top down call graph. |
| 189 */ | |
| 190 devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() { | |
| 191 this.topDownTree_.computeTotalWeights(); | |
| 192 return this.topDownTree_.getRoot(); | |
| 193 }; | |
| 194 | |
| 195 | |
| 196 /** | |
| 197 * Returns the root of the bottom up call graph. | |
| 198 */ | |
| 199 devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() { | |
| 200 this.bottomUpTree_.computeTotalWeights(); | |
| 201 return this.bottomUpTree_.getRoot(); | |
| 202 }; | |
| 203 | |
| 204 | |
| 205 /** | |
| 206 * Traverses the top down call graph in preorder. | |
| 207 * | 189 * |
| 208 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 190 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
| 209 */ | 191 */ |
| 210 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { | 192 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { |
| 211 this.topDownTree_.traverse(f); | 193 this.topDownTree_.traverse(f); |
| 212 }; | 194 }; |
| 213 | 195 |
| 214 | 196 |
| 215 /** | 197 /** |
| 216 * Traverses the bottom up call graph in preorder. | 198 * Performs a BF traversal of the bottom up call graph. |
| 217 * | 199 * |
| 218 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 200 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
| 219 */ | 201 */ |
| 220 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { | 202 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { |
| 221 this.bottomUpTree_.traverse(f); | 203 this.bottomUpTree_.traverse(f); |
| 222 }; | 204 }; |
| 223 | 205 |
| 224 | 206 |
| 225 /** | 207 /** |
| 226 * Calculates a top down profile starting from the specified node. | 208 * Calculates a top down profile for a node with the specified label. |
| 209 * If no name specified, returns the whole top down calls tree. |
| 227 * | 210 * |
| 228 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. | 211 * @param {string} opt_label Node label. |
| 229 */ | 212 */ |
| 230 devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_root) { | 213 devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_label) { |
| 231 if (!opt_root) { | 214 return this.getTreeProfile_(this.topDownTree_, opt_label); |
| 232 this.topDownTree_.computeTotalWeights(); | 215 }; |
| 233 return this.topDownTree_; | 216 |
| 217 |
| 218 /** |
| 219 * Calculates a bottom up profile for a node with the specified label. |
| 220 * If no name specified, returns the whole bottom up calls tree. |
| 221 * |
| 222 * @param {string} opt_label Node label. |
| 223 */ |
| 224 devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_label) { |
| 225 return this.getTreeProfile_(this.bottomUpTree_, opt_label); |
| 226 }; |
| 227 |
| 228 |
| 229 /** |
| 230 * Helper function for calculating a tree profile. |
| 231 * |
| 232 * @param {devtools.profiler.Profile.CallTree} tree Call tree. |
| 233 * @param {string} opt_label Node label. |
| 234 */ |
| 235 devtools.profiler.Profile.prototype.getTreeProfile_ = function(tree, opt_label)
{ |
| 236 if (!opt_label) { |
| 237 tree.computeTotalWeights(); |
| 238 return tree; |
| 234 } else { | 239 } else { |
| 235 throw Error('not implemented'); | 240 var subTree = tree.cloneSubtree(opt_label); |
| 241 subTree.computeTotalWeights(); |
| 242 return subTree; |
| 236 } | 243 } |
| 237 }; | 244 }; |
| 238 | 245 |
| 239 | 246 |
| 240 /** | 247 /** |
| 241 * Calculates a bottom up profile starting from the specified node. | 248 * Calculates a flat profile of callees starting from a node with |
| 249 * the specified label. If no name specified, starts from the root. |
| 242 * | 250 * |
| 243 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. | 251 * @param {string} opt_label Starting node label. |
| 244 */ | 252 */ |
| 245 devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_root) { | 253 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_label) { |
| 246 if (!opt_root) { | 254 var counters = new devtools.profiler.CallTree(); |
| 247 this.bottomUpTree_.computeTotalWeights(); | 255 var rootLabel = opt_label || devtools.profiler.CallTree.ROOT_NODE_LABEL; |
| 248 return this.bottomUpTree_; | 256 var precs = {}; |
| 249 } else { | 257 precs[rootLabel] = 0; |
| 250 throw Error('not implemented'); | 258 var root = counters.findOrAddChild(rootLabel); |
| 251 } | |
| 252 }; | |
| 253 | 259 |
| 254 | |
| 255 /** | |
| 256 * Calculates a flat profile of callees starting from the specified node. | |
| 257 * | |
| 258 * @param {devtools.profiler.CallTree.Node} opt_root Starting node. | |
| 259 */ | |
| 260 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) { | |
| 261 var counters = new devtools.profiler.CallTree(); | |
| 262 var precs = {}; | |
| 263 this.topDownTree_.computeTotalWeights(); | 260 this.topDownTree_.computeTotalWeights(); |
| 264 this.topDownTree_.traverseInDepth( | 261 this.topDownTree_.traverseInDepth( |
| 265 function onEnter(node) { | 262 function onEnter(node) { |
| 266 if (!(node.label in precs)) { | 263 if (!(node.label in precs)) { |
| 267 precs[node.label] = 0; | 264 precs[node.label] = 0; |
| 268 } | 265 } |
| 269 var rec = counters.findOrAddChild(node.label); | 266 var nodeLabelIsRootLabel = node.label == rootLabel; |
| 270 rec.selfWeight += node.selfWeight; | 267 if (nodeLabelIsRootLabel || precs[rootLabel] > 0) { |
| 271 if (precs[node.label] == 0) { | 268 if (precs[rootLabel] == 0) { |
| 272 rec.totalWeight += node.totalWeight; | 269 root.selfWeight += node.selfWeight; |
| 270 root.totalWeight += node.totalWeight; |
| 271 } else { |
| 272 var rec = root.findOrAddChild(node.label); |
| 273 rec.selfWeight += node.selfWeight; |
| 274 if (nodeLabelIsRootLabel || precs[node.label] == 0) { |
| 275 rec.totalWeight += node.totalWeight; |
| 276 } |
| 277 } |
| 278 precs[node.label]++; |
| 273 } | 279 } |
| 274 precs[node.label]++; | |
| 275 }, | 280 }, |
| 276 function onExit(node) { | 281 function onExit(node) { |
| 277 precs[node.label]--; | 282 if (node.label == rootLabel || precs[rootLabel] > 0) { |
| 283 precs[node.label]--; |
| 284 } |
| 278 }, | 285 }, |
| 279 opt_root); | 286 null); |
| 287 |
| 288 if (!opt_label) { |
| 289 // If we have created a flat profile for the whole program, we don't |
| 290 // need an explicit root in it. Thus, replace the counters tree |
| 291 // root with the node corresponding to the whole program. |
| 292 counters.root_ = root; |
| 293 } else { |
| 294 // Propagate weights so percents can be calculated correctly. |
| 295 counters.getRoot().selfWeight = root.selfWeight; |
| 296 counters.getRoot().totalWeight = root.totalWeight; |
| 297 } |
| 280 return counters; | 298 return counters; |
| 281 }; | 299 }; |
| 282 | 300 |
| 283 | 301 |
| 284 /** | 302 /** |
| 285 * Creates a dynamic code entry. | 303 * Creates a dynamic code entry. |
| 286 * | 304 * |
| 287 * @param {number} size Code size. | 305 * @param {number} size Code size. |
| 288 * @param {string} type Code type. | 306 * @param {string} type Code type. |
| 289 * @param {string} name Function name. | 307 * @param {string} name Function name. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 309 return this.type + ': ' + name; | 327 return this.type + ': ' + name; |
| 310 }; | 328 }; |
| 311 | 329 |
| 312 | 330 |
| 313 /** | 331 /** |
| 314 * Constructs a call graph. | 332 * Constructs a call graph. |
| 315 * | 333 * |
| 316 * @constructor | 334 * @constructor |
| 317 */ | 335 */ |
| 318 devtools.profiler.CallTree = function() { | 336 devtools.profiler.CallTree = function() { |
| 319 this.root_ = new devtools.profiler.CallTree.Node(''); | 337 this.root_ = new devtools.profiler.CallTree.Node( |
| 338 devtools.profiler.CallTree.ROOT_NODE_LABEL); |
| 320 }; | 339 }; |
| 321 | 340 |
| 322 | 341 |
| 323 /** | 342 /** |
| 343 * The label of the root node. |
| 344 */ |
| 345 devtools.profiler.CallTree.ROOT_NODE_LABEL = ''; |
| 346 |
| 347 |
| 348 /** |
| 324 * @private | 349 * @private |
| 325 */ | 350 */ |
| 326 devtools.profiler.CallTree.prototype.totalsComputed_ = false; | 351 devtools.profiler.CallTree.prototype.totalsComputed_ = false; |
| 327 | 352 |
| 328 | 353 |
| 329 /** | 354 /** |
| 330 * Returns the tree root. | 355 * Returns the tree root. |
| 331 */ | 356 */ |
| 332 devtools.profiler.CallTree.prototype.getRoot = function() { | 357 devtools.profiler.CallTree.prototype.getRoot = function() { |
| 333 return this.root_; | 358 return this.root_; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 352 }; | 377 }; |
| 353 | 378 |
| 354 | 379 |
| 355 /** | 380 /** |
| 356 * Finds an immediate child of the specified parent with the specified | 381 * 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 | 382 * label, creates a child node if necessary. If a parent node isn't |
| 358 * specified, uses tree root. | 383 * specified, uses tree root. |
| 359 * | 384 * |
| 360 * @param {string} label Child node label. | 385 * @param {string} label Child node label. |
| 361 */ | 386 */ |
| 362 devtools.profiler.CallTree.prototype.findOrAddChild = function( | 387 devtools.profiler.CallTree.prototype.findOrAddChild = function(label) { |
| 363 label, opt_parent) { | 388 return this.root_.findOrAddChild(label); |
| 364 var parent = opt_parent || this.root_; | |
| 365 return parent.findOrAddChild(label); | |
| 366 }; | 389 }; |
| 367 | 390 |
| 368 | 391 |
| 392 /** |
| 393 * Creates a subtree by cloning and merging all subtrees rooted at nodes |
| 394 * with a given label. E.g. cloning the following call tree on label 'A' |
| 395 * will give the following result: |
| 396 * |
| 397 * <A>--<B> <B> |
| 398 * / / |
| 399 * <root> == clone on 'A' ==> <root>--<A> |
| 400 * \ \ |
| 401 * <C>--<A>--<D> <D> |
| 402 * |
| 403 * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the |
| 404 * source call tree. |
| 405 * |
| 406 * @param {string} label The label of the new root node. |
| 407 */ |
| 408 devtools.profiler.CallTree.prototype.cloneSubtree = function(label) { |
| 409 var subTree = new devtools.profiler.CallTree(); |
| 410 this.traverse(function(node, parent) { |
| 411 if (!parent && node.label != label) { |
| 412 return null; |
| 413 } |
| 414 var child = (parent ? parent : subTree).findOrAddChild(node.label); |
| 415 child.selfWeight += node.selfWeight; |
| 416 return child; |
| 417 }); |
| 418 return subTree; |
| 419 }; |
| 420 |
| 421 |
| 369 /** | 422 /** |
| 370 * Computes total weights in the call graph. | 423 * Computes total weights in the call graph. |
| 371 */ | 424 */ |
| 372 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { | 425 devtools.profiler.CallTree.prototype.computeTotalWeights = function() { |
| 373 if (this.totalsComputed_) { | 426 if (this.totalsComputed_) { |
| 374 return; | 427 return; |
| 375 } | 428 } |
| 376 this.root_.computeTotalWeight(); | 429 this.root_.computeTotalWeight(); |
| 377 this.totalsComputed_ = true; | 430 this.totalsComputed_ = true; |
| 378 }; | 431 }; |
| 379 | 432 |
| 380 | 433 |
| 381 /** | 434 /** |
| 382 * Traverses the call graph in preorder. This function can be used for | 435 * Traverses the call graph in preorder. This function can be used for |
| 383 * building optionally modified tree clones. This is the boilerplate code | 436 * building optionally modified tree clones. This is the boilerplate code |
| 384 * for this scenario: | 437 * for this scenario: |
| 385 * | 438 * |
| 386 * callTree.traverse(function(node, parentClone) { | 439 * callTree.traverse(function(node, parentClone) { |
| 387 * var nodeClone = cloneNode(node); | 440 * var nodeClone = cloneNode(node); |
| 388 * if (parentClone) | 441 * if (parentClone) |
| 389 * parentClone.addChild(nodeClone); | 442 * parentClone.addChild(nodeClone); |
| 390 * return nodeClone; | 443 * return nodeClone; |
| 391 * }); | 444 * }); |
| 392 * | 445 * |
| 393 * @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. | 446 * @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. |
| 394 * The second parameter is the result of calling 'f' on the parent node. | 447 * The second parameter is the result of calling 'f' on the parent node. |
| 395 * @param {devtools.profiler.CallTree.Node} opt_start Starting node. | |
| 396 */ | 448 */ |
| 397 devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { | 449 devtools.profiler.CallTree.prototype.traverse = function(f) { |
| 398 var pairsToProcess = new ConsArray(); | 450 var pairsToProcess = new ConsArray(); |
| 399 pairsToProcess.concat([{node: opt_start || this.root_, param: null}]); | 451 pairsToProcess.concat([{node: this.root_, param: null}]); |
| 400 while (!pairsToProcess.atEnd()) { | 452 while (!pairsToProcess.atEnd()) { |
| 401 var pair = pairsToProcess.next(); | 453 var pair = pairsToProcess.next(); |
| 402 var node = pair.node; | 454 var node = pair.node; |
| 403 var newParam = f(node, pair.param); | 455 var newParam = f(node, pair.param); |
| 404 var morePairsToProcess = []; | 456 var morePairsToProcess = []; |
| 405 node.forEachChild(function (child) { | 457 node.forEachChild(function (child) { |
| 406 morePairsToProcess.push({node: child, param: newParam}); }); | 458 morePairsToProcess.push({node: child, param: newParam}); }); |
| 407 pairsToProcess.concat(morePairsToProcess); | 459 pairsToProcess.concat(morePairsToProcess); |
| 408 } | 460 } |
| 409 }; | 461 }; |
| 410 | 462 |
| 411 | 463 |
| 412 /** | 464 /** |
| 413 * Performs an indepth call graph traversal. | 465 * Performs an indepth call graph traversal. |
| 414 * | 466 * |
| 415 * @param {function(devtools.profiler.CallTree.Node)} enter A function called | 467 * @param {function(devtools.profiler.CallTree.Node)} enter A function called |
| 416 * prior to visiting node's children. | 468 * prior to visiting node's children. |
| 417 * @param {function(devtools.profiler.CallTree.Node)} exit A function called | 469 * @param {function(devtools.profiler.CallTree.Node)} exit A function called |
| 418 * after visiting node's children. | 470 * after visiting node's children. |
| 419 * @param {devtools.profiler.CallTree.Node} opt_start Starting node. | |
| 420 */ | 471 */ |
| 421 devtools.profiler.CallTree.prototype.traverseInDepth = function( | 472 devtools.profiler.CallTree.prototype.traverseInDepth = function(enter, exit) { |
| 422 enter, exit, opt_start) { | |
| 423 function traverse(node) { | 473 function traverse(node) { |
| 424 enter(node); | 474 enter(node); |
| 425 node.forEachChild(traverse); | 475 node.forEachChild(traverse); |
| 426 exit(node); | 476 exit(node); |
| 427 } | 477 } |
| 428 traverse(opt_start || this.root_); | 478 traverse(this.root_); |
| 429 }; | 479 }; |
| 430 | 480 |
| 431 | 481 |
| 432 /** | 482 /** |
| 433 * Constructs a call graph node. | 483 * Constructs a call graph node. |
| 434 * | 484 * |
| 435 * @param {string} label Node label. | 485 * @param {string} label Node label. |
| 436 * @param {devtools.profiler.CallTree.Node} opt_parent Node parent. | 486 * @param {devtools.profiler.CallTree.Node} opt_parent Node parent. |
| 437 */ | 487 */ |
| 438 devtools.profiler.CallTree.Node = function(label, opt_parent) { | 488 devtools.profiler.CallTree.Node = function(label, opt_parent) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 500 return this.children[label] || null; | 550 return this.children[label] || null; |
| 501 }; | 551 }; |
| 502 | 552 |
| 503 | 553 |
| 504 /** | 554 /** |
| 505 * Finds an immediate child with the specified label, creates a child | 555 * Finds an immediate child with the specified label, creates a child |
| 506 * node if necessary. | 556 * node if necessary. |
| 507 * | 557 * |
| 508 * @param {string} label Child node label. | 558 * @param {string} label Child node label. |
| 509 */ | 559 */ |
| 510 devtools.profiler.CallTree.Node.prototype.findOrAddChild = function( | 560 devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(label) { |
| 511 label) { | |
| 512 return this.findChild(label) || this.addChild(label); | 561 return this.findChild(label) || this.addChild(label); |
| 513 }; | 562 }; |
| 514 | 563 |
| 515 | 564 |
| 516 /** | 565 /** |
| 517 * Calls the specified function for every child. | 566 * Calls the specified function for every child. |
| 518 * | 567 * |
| 519 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. | 568 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
| 520 */ | 569 */ |
| 521 devtools.profiler.CallTree.Node.prototype.forEachChild = function(f) { | 570 devtools.profiler.CallTree.Node.prototype.forEachChild = function(f) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 547 labels, opt_f) { | 596 labels, opt_f) { |
| 548 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { | 597 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) { |
| 549 var child = curr.findChild(labels[pos]); | 598 var child = curr.findChild(labels[pos]); |
| 550 if (opt_f) { | 599 if (opt_f) { |
| 551 opt_f(child, pos); | 600 opt_f(child, pos); |
| 552 } | 601 } |
| 553 curr = child; | 602 curr = child; |
| 554 } | 603 } |
| 555 return curr; | 604 return curr; |
| 556 }; | 605 }; |
| OLD | NEW |