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 |