Upstream version 7.36.151.0
[platform/framework/web/crosswalk.git] / src / v8 / tools / profile.js
1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
27
28
29 /**
30  * Creates a profile object for processing profiling-related events
31  * and calculating function execution times.
32  *
33  * @constructor
34  */
35 function Profile() {
36   this.codeMap_ = new CodeMap();
37   this.topDownTree_ = new CallTree();
38   this.bottomUpTree_ = new CallTree();
39 };
40
41
42 /**
43  * Returns whether a function with the specified name must be skipped.
44  * Should be overriden by subclasses.
45  *
46  * @param {string} name Function name.
47  */
48 Profile.prototype.skipThisFunction = function(name) {
49   return false;
50 };
51
52
53 /**
54  * Enum for profiler operations that involve looking up existing
55  * code entries.
56  *
57  * @enum {number}
58  */
59 Profile.Operation = {
60   MOVE: 0,
61   DELETE: 1,
62   TICK: 2
63 };
64
65
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 /**
79  * Called whenever the specified operation has failed finding a function
80  * containing the specified address. Should be overriden by subclasses.
81  * See the Profile.Operation enum for the list of
82  * possible operations.
83  *
84  * @param {number} operation Operation.
85  * @param {number} addr Address of the unknown code.
86  * @param {number} opt_stackPos If an unknown address is encountered
87  *     during stack strace processing, specifies a position of the frame
88  *     containing the address.
89  */
90 Profile.prototype.handleUnknownCode = function(
91     operation, addr, opt_stackPos) {
92 };
93
94
95 /**
96  * Registers a library.
97  *
98  * @param {string} name Code entry name.
99  * @param {number} startAddr Starting address.
100  * @param {number} endAddr Ending address.
101  */
102 Profile.prototype.addLibrary = function(
103     name, startAddr, endAddr) {
104   var entry = new CodeMap.CodeEntry(
105       endAddr - startAddr, name);
106   this.codeMap_.addLibrary(startAddr, entry);
107   return entry;
108 };
109
110
111 /**
112  * Registers statically compiled code entry.
113  *
114  * @param {string} name Code entry name.
115  * @param {number} startAddr Starting address.
116  * @param {number} endAddr Ending address.
117  */
118 Profile.prototype.addStaticCode = function(
119     name, startAddr, endAddr) {
120   var entry = new CodeMap.CodeEntry(
121       endAddr - startAddr, name);
122   this.codeMap_.addStaticCode(startAddr, entry);
123   return entry;
124 };
125
126
127 /**
128  * Registers dynamic (JIT-compiled) code entry.
129  *
130  * @param {string} type Code entry type.
131  * @param {string} name Code entry name.
132  * @param {number} start Starting address.
133  * @param {number} size Code entry size.
134  */
135 Profile.prototype.addCode = function(
136     type, name, start, size) {
137   var entry = new Profile.DynamicCodeEntry(size, type, name);
138   this.codeMap_.addCode(start, entry);
139   return entry;
140 };
141
142
143 /**
144  * Registers dynamic (JIT-compiled) code entry.
145  *
146  * @param {string} type Code entry type.
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.
152  */
153 Profile.prototype.addFuncCode = function(
154     type, name, start, size, funcAddr, state) {
155   // As code and functions are in the same address space,
156   // it is safe to put them in a single code map.
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;
164   }
165   var entry = this.codeMap_.findDynamicEntryByStartAddress(start);
166   if (entry) {
167     if (entry.size === size && entry.func === func) {
168       // Entry state has changed.
169       entry.state = state;
170     }
171   } else {
172     entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
173     this.codeMap_.addCode(start, entry);
174   }
175   return entry;
176 };
177
178
179 /**
180  * Reports about moving of a dynamic code entry.
181  *
182  * @param {number} from Current code entry address.
183  * @param {number} to New code entry address.
184  */
185 Profile.prototype.moveCode = function(from, to) {
186   try {
187     this.codeMap_.moveCode(from, to);
188   } catch (e) {
189     this.handleUnknownCode(Profile.Operation.MOVE, from);
190   }
191 };
192
193
194 /**
195  * Reports about deletion of a dynamic code entry.
196  *
197  * @param {number} start Starting address.
198  */
199 Profile.prototype.deleteCode = function(start) {
200   try {
201     this.codeMap_.deleteCode(start);
202   } catch (e) {
203     this.handleUnknownCode(Profile.Operation.DELETE, start);
204   }
205 };
206
207
208 /**
209  * Reports about moving of a dynamic code entry.
210  *
211  * @param {number} from Current code entry address.
212  * @param {number} to New code entry address.
213  */
214 Profile.prototype.moveFunc = function(from, to) {
215   if (this.codeMap_.findDynamicEntryByStartAddress(from)) {
216     this.codeMap_.moveCode(from, to);
217   }
218 };
219
220
221 /**
222  * Retrieves a code entry by an address.
223  *
224  * @param {number} addr Entry address.
225  */
226 Profile.prototype.findEntry = function(addr) {
227   return this.codeMap_.findEntry(addr);
228 };
229
230
231 /**
232  * Records a tick event. Stack must contain a sequence of
233  * addresses starting with the program counter value.
234  *
235  * @param {Array<number>} stack Stack sample.
236  */
237 Profile.prototype.recordTick = function(stack) {
238   var processedStack = this.resolveAndFilterFuncs_(stack);
239   this.bottomUpTree_.addPath(processedStack);
240   processedStack.reverse();
241   this.topDownTree_.addPath(processedStack);
242 };
243
244
245 /**
246  * Translates addresses into function names and filters unneeded
247  * functions.
248  *
249  * @param {Array<number>} stack Stack sample.
250  */
251 Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
252   var result = [];
253   for (var i = 0; i < stack.length; ++i) {
254     var entry = this.codeMap_.findEntry(stack[i]);
255     if (entry) {
256       var name = entry.getName();
257       if (!this.skipThisFunction(name)) {
258         result.push(name);
259       }
260     } else {
261       this.handleUnknownCode(
262           Profile.Operation.TICK, stack[i], i);
263     }
264   }
265   return result;
266 };
267
268
269 /**
270  * Performs a BF traversal of the top down call graph.
271  *
272  * @param {function(CallTree.Node)} f Visitor function.
273  */
274 Profile.prototype.traverseTopDownTree = function(f) {
275   this.topDownTree_.traverse(f);
276 };
277
278
279 /**
280  * Performs a BF traversal of the bottom up call graph.
281  *
282  * @param {function(CallTree.Node)} f Visitor function.
283  */
284 Profile.prototype.traverseBottomUpTree = function(f) {
285   this.bottomUpTree_.traverse(f);
286 };
287
288
289 /**
290  * Calculates a top down profile for a node with the specified label.
291  * If no name specified, returns the whole top down calls tree.
292  *
293  * @param {string} opt_label Node label.
294  */
295 Profile.prototype.getTopDownProfile = function(opt_label) {
296   return this.getTreeProfile_(this.topDownTree_, opt_label);
297 };
298
299
300 /**
301  * Calculates a bottom up profile for a node with the specified label.
302  * If no name specified, returns the whole bottom up calls tree.
303  *
304  * @param {string} opt_label Node label.
305  */
306 Profile.prototype.getBottomUpProfile = function(opt_label) {
307   return this.getTreeProfile_(this.bottomUpTree_, opt_label);
308 };
309
310
311 /**
312  * Helper function for calculating a tree profile.
313  *
314  * @param {Profile.CallTree} tree Call tree.
315  * @param {string} opt_label Node label.
316  */
317 Profile.prototype.getTreeProfile_ = function(tree, opt_label) {
318   if (!opt_label) {
319     tree.computeTotalWeights();
320     return tree;
321   } else {
322     var subTree = tree.cloneSubtree(opt_label);
323     subTree.computeTotalWeights();
324     return subTree;
325   }
326 };
327
328
329 /**
330  * Calculates a flat profile of callees starting from a node with
331  * the specified label. If no name specified, starts from the root.
332  *
333  * @param {string} opt_label Starting node label.
334  */
335 Profile.prototype.getFlatProfile = function(opt_label) {
336   var counters = new CallTree();
337   var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL;
338   var precs = {};
339   precs[rootLabel] = 0;
340   var root = counters.findOrAddChild(rootLabel);
341
342   this.topDownTree_.computeTotalWeights();
343   this.topDownTree_.traverseInDepth(
344     function onEnter(node) {
345       if (!(node.label in precs)) {
346         precs[node.label] = 0;
347       }
348       var nodeLabelIsRootLabel = node.label == rootLabel;
349       if (nodeLabelIsRootLabel || precs[rootLabel] > 0) {
350         if (precs[rootLabel] == 0) {
351           root.selfWeight += node.selfWeight;
352           root.totalWeight += node.totalWeight;
353         } else {
354           var rec = root.findOrAddChild(node.label);
355           rec.selfWeight += node.selfWeight;
356           if (nodeLabelIsRootLabel || precs[node.label] == 0) {
357             rec.totalWeight += node.totalWeight;
358           }
359         }
360         precs[node.label]++;
361       }
362     },
363     function onExit(node) {
364       if (node.label == rootLabel || precs[rootLabel] > 0) {
365         precs[node.label]--;
366       }
367     },
368     null);
369
370   if (!opt_label) {
371     // If we have created a flat profile for the whole program, we don't
372     // need an explicit root in it. Thus, replace the counters tree
373     // root with the node corresponding to the whole program.
374     counters.root_ = root;
375   } else {
376     // Propagate weights so percents can be calculated correctly.
377     counters.getRoot().selfWeight = root.selfWeight;
378     counters.getRoot().totalWeight = root.totalWeight;
379   }
380   return counters;
381 };
382
383
384 /**
385  * Cleans up function entries that are not referenced by code entries.
386  */
387 Profile.prototype.cleanUpFuncEntries = function() {
388   var referencedFuncEntries = [];
389   var entries = this.codeMap_.getAllDynamicEntriesWithAddresses();
390   for (var i = 0, l = entries.length; i < l; ++i) {
391     if (entries[i][1].constructor === Profile.FunctionEntry) {
392       entries[i][1].used = false;
393     }
394   }
395   for (var i = 0, l = entries.length; i < l; ++i) {
396     if ("func" in entries[i][1]) {
397       entries[i][1].func.used = true;
398     }
399   }
400   for (var i = 0, l = entries.length; i < l; ++i) {
401     if (entries[i][1].constructor === Profile.FunctionEntry &&
402         !entries[i][1].used) {
403       this.codeMap_.deleteCode(entries[i][0]);
404     }
405   }
406 };
407
408
409 /**
410  * Creates a dynamic code entry.
411  *
412  * @param {number} size Code size.
413  * @param {string} type Code type.
414  * @param {string} name Function name.
415  * @constructor
416  */
417 Profile.DynamicCodeEntry = function(size, type, name) {
418   CodeMap.CodeEntry.call(this, size, name);
419   this.type = type;
420 };
421
422
423 /**
424  * Returns node name.
425  */
426 Profile.DynamicCodeEntry.prototype.getName = function() {
427   return this.type + ': ' + this.name;
428 };
429
430
431 /**
432  * Returns raw node name (without type decoration).
433  */
434 Profile.DynamicCodeEntry.prototype.getRawName = function() {
435   return this.name;
436 };
437
438
439 Profile.DynamicCodeEntry.prototype.isJSFunction = function() {
440   return false;
441 };
442
443
444 Profile.DynamicCodeEntry.prototype.toString = function() {
445   return this.getName() + ': ' + this.size.toString(16);
446 };
447
448
449 /**
450  * Creates a dynamic code entry.
451  *
452  * @param {number} size Code size.
453  * @param {string} type Code type.
454  * @param {Profile.FunctionEntry} func Shared function entry.
455  * @param {Profile.CodeState} state Code optimization state.
456  * @constructor
457  */
458 Profile.DynamicFuncCodeEntry = function(size, type, func, state) {
459   CodeMap.CodeEntry.call(this, size);
460   this.type = type;
461   this.func = func;
462   this.state = state;
463 };
464
465 Profile.DynamicFuncCodeEntry.STATE_PREFIX = ["", "~", "*"];
466
467 /**
468  * Returns node name.
469  */
470 Profile.DynamicFuncCodeEntry.prototype.getName = function() {
471   var name = this.func.getName();
472   return this.type + ': ' + Profile.DynamicFuncCodeEntry.STATE_PREFIX[this.state] + name;
473 };
474
475
476 /**
477  * Returns raw node name (without type decoration).
478  */
479 Profile.DynamicFuncCodeEntry.prototype.getRawName = function() {
480   return this.func.getName();
481 };
482
483
484 Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function() {
485   return true;
486 };
487
488
489 Profile.DynamicFuncCodeEntry.prototype.toString = function() {
490   return this.getName() + ': ' + this.size.toString(16);
491 };
492
493
494 /**
495  * Creates a shared function object entry.
496  *
497  * @param {string} name Function name.
498  * @constructor
499  */
500 Profile.FunctionEntry = function(name) {
501   CodeMap.CodeEntry.call(this, 0, name);
502 };
503
504
505 /**
506  * Returns node name.
507  */
508 Profile.FunctionEntry.prototype.getName = function() {
509   var name = this.name;
510   if (name.length == 0) {
511     name = '<anonymous>';
512   } else if (name.charAt(0) == ' ') {
513     // An anonymous function with location: " aaa.js:10".
514     name = '<anonymous>' + name;
515   }
516   return name;
517 };
518
519 Profile.FunctionEntry.prototype.toString = CodeMap.CodeEntry.prototype.toString;
520
521 /**
522  * Constructs a call graph.
523  *
524  * @constructor
525  */
526 function CallTree() {
527   this.root_ = new CallTree.Node(
528       CallTree.ROOT_NODE_LABEL);
529 };
530
531
532 /**
533  * The label of the root node.
534  */
535 CallTree.ROOT_NODE_LABEL = '';
536
537
538 /**
539  * @private
540  */
541 CallTree.prototype.totalsComputed_ = false;
542
543
544 /**
545  * Returns the tree root.
546  */
547 CallTree.prototype.getRoot = function() {
548   return this.root_;
549 };
550
551
552 /**
553  * Adds the specified call path, constructing nodes as necessary.
554  *
555  * @param {Array<string>} path Call path.
556  */
557 CallTree.prototype.addPath = function(path) {
558   if (path.length == 0) {
559     return;
560   }
561   var curr = this.root_;
562   for (var i = 0; i < path.length; ++i) {
563     curr = curr.findOrAddChild(path[i]);
564   }
565   curr.selfWeight++;
566   this.totalsComputed_ = false;
567 };
568
569
570 /**
571  * Finds an immediate child of the specified parent with the specified
572  * label, creates a child node if necessary. If a parent node isn't
573  * specified, uses tree root.
574  *
575  * @param {string} label Child node label.
576  */
577 CallTree.prototype.findOrAddChild = function(label) {
578   return this.root_.findOrAddChild(label);
579 };
580
581
582 /**
583  * Creates a subtree by cloning and merging all subtrees rooted at nodes
584  * with a given label. E.g. cloning the following call tree on label 'A'
585  * will give the following result:
586  *
587  *           <A>--<B>                                     <B>
588  *          /                                            /
589  *     <root>             == clone on 'A' ==>  <root>--<A>
590  *          \                                            \
591  *           <C>--<A>--<D>                                <D>
592  *
593  * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the
594  * source call tree.
595  *
596  * @param {string} label The label of the new root node.
597  */
598 CallTree.prototype.cloneSubtree = function(label) {
599   var subTree = new CallTree();
600   this.traverse(function(node, parent) {
601     if (!parent && node.label != label) {
602       return null;
603     }
604     var child = (parent ? parent : subTree).findOrAddChild(node.label);
605     child.selfWeight += node.selfWeight;
606     return child;
607   });
608   return subTree;
609 };
610
611
612 /**
613  * Computes total weights in the call graph.
614  */
615 CallTree.prototype.computeTotalWeights = function() {
616   if (this.totalsComputed_) {
617     return;
618   }
619   this.root_.computeTotalWeight();
620   this.totalsComputed_ = true;
621 };
622
623
624 /**
625  * Traverses the call graph in preorder. This function can be used for
626  * building optionally modified tree clones. This is the boilerplate code
627  * for this scenario:
628  *
629  * callTree.traverse(function(node, parentClone) {
630  *   var nodeClone = cloneNode(node);
631  *   if (parentClone)
632  *     parentClone.addChild(nodeClone);
633  *   return nodeClone;
634  * });
635  *
636  * @param {function(CallTree.Node, *)} f Visitor function.
637  *    The second parameter is the result of calling 'f' on the parent node.
638  */
639 CallTree.prototype.traverse = function(f) {
640   var pairsToProcess = new ConsArray();
641   pairsToProcess.concat([{node: this.root_, param: null}]);
642   while (!pairsToProcess.atEnd()) {
643     var pair = pairsToProcess.next();
644     var node = pair.node;
645     var newParam = f(node, pair.param);
646     var morePairsToProcess = [];
647     node.forEachChild(function (child) {
648         morePairsToProcess.push({node: child, param: newParam}); });
649     pairsToProcess.concat(morePairsToProcess);
650   }
651 };
652
653
654 /**
655  * Performs an indepth call graph traversal.
656  *
657  * @param {function(CallTree.Node)} enter A function called
658  *     prior to visiting node's children.
659  * @param {function(CallTree.Node)} exit A function called
660  *     after visiting node's children.
661  */
662 CallTree.prototype.traverseInDepth = function(enter, exit) {
663   function traverse(node) {
664     enter(node);
665     node.forEachChild(traverse);
666     exit(node);
667   }
668   traverse(this.root_);
669 };
670
671
672 /**
673  * Constructs a call graph node.
674  *
675  * @param {string} label Node label.
676  * @param {CallTree.Node} opt_parent Node parent.
677  */
678 CallTree.Node = function(label, opt_parent) {
679   this.label = label;
680   this.parent = opt_parent;
681   this.children = {};
682 };
683
684
685 /**
686  * Node self weight (how many times this node was the last node in
687  * a call path).
688  * @type {number}
689  */
690 CallTree.Node.prototype.selfWeight = 0;
691
692
693 /**
694  * Node total weight (includes weights of all children).
695  * @type {number}
696  */
697 CallTree.Node.prototype.totalWeight = 0;
698
699
700 /**
701  * Adds a child node.
702  *
703  * @param {string} label Child node label.
704  */
705 CallTree.Node.prototype.addChild = function(label) {
706   var child = new CallTree.Node(label, this);
707   this.children[label] = child;
708   return child;
709 };
710
711
712 /**
713  * Computes node's total weight.
714  */
715 CallTree.Node.prototype.computeTotalWeight =
716     function() {
717   var totalWeight = this.selfWeight;
718   this.forEachChild(function(child) {
719       totalWeight += child.computeTotalWeight(); });
720   return this.totalWeight = totalWeight;
721 };
722
723
724 /**
725  * Returns all node's children as an array.
726  */
727 CallTree.Node.prototype.exportChildren = function() {
728   var result = [];
729   this.forEachChild(function (node) { result.push(node); });
730   return result;
731 };
732
733
734 /**
735  * Finds an immediate child with the specified label.
736  *
737  * @param {string} label Child node label.
738  */
739 CallTree.Node.prototype.findChild = function(label) {
740   return this.children[label] || null;
741 };
742
743
744 /**
745  * Finds an immediate child with the specified label, creates a child
746  * node if necessary.
747  *
748  * @param {string} label Child node label.
749  */
750 CallTree.Node.prototype.findOrAddChild = function(label) {
751   return this.findChild(label) || this.addChild(label);
752 };
753
754
755 /**
756  * Calls the specified function for every child.
757  *
758  * @param {function(CallTree.Node)} f Visitor function.
759  */
760 CallTree.Node.prototype.forEachChild = function(f) {
761   for (var c in this.children) {
762     f(this.children[c]);
763   }
764 };
765
766
767 /**
768  * Walks up from the current node up to the call tree root.
769  *
770  * @param {function(CallTree.Node)} f Visitor function.
771  */
772 CallTree.Node.prototype.walkUpToRoot = function(f) {
773   for (var curr = this; curr != null; curr = curr.parent) {
774     f(curr);
775   }
776 };
777
778
779 /**
780  * Tries to find a node with the specified path.
781  *
782  * @param {Array<string>} labels The path.
783  * @param {function(CallTree.Node)} opt_f Visitor function.
784  */
785 CallTree.Node.prototype.descendToChild = function(
786     labels, opt_f) {
787   for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) {
788     var child = curr.findChild(labels[pos]);
789     if (opt_f) {
790       opt_f(child, pos);
791     }
792     curr = child;
793   }
794   return curr;
795 };