Home | History | Annotate | Download | only in tools
      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 // Load source code files from <project root>/tools.
     29 // Files: tools/splaytree.js tools/codemap.js tools/consarray.js tools/profile.js
     30 
     31 
     32 function stackToString(stack) {
     33   return stack.join(' -> ');
     34 };
     35 
     36 
     37 function assertPathExists(root, path, opt_message) {
     38   var message = opt_message ? ' (' + opt_message + ')' : '';
     39   assertNotNull(root.descendToChild(path, function(node, pos) {
     40     assertNotNull(node,
     41       stackToString(path.slice(0, pos)) + ' has no child ' +
     42                     path[pos] + message);
     43   }), opt_message);
     44 };
     45 
     46 
     47 function assertNoPathExists(root, path, opt_message) {
     48   var message = opt_message ? ' (' + opt_message + ')' : '';
     49   assertNull(root.descendToChild(path), opt_message);
     50 };
     51 
     52 
     53 function countNodes(profile, traverseFunc) {
     54   var count = 0;
     55   traverseFunc.call(profile, function () { count++; });
     56   return count;
     57 };
     58 
     59 
     60 function ProfileTestDriver() {
     61   this.profile = new Profile();
     62   this.stack_ = [];
     63   this.addFunctions_();
     64 };
     65 
     66 
     67 // Addresses inside functions.
     68 ProfileTestDriver.prototype.funcAddrs_ = {
     69     'lib1-f1': 0x11110, 'lib1-f2': 0x11210,
     70     'lib2-f1': 0x21110, 'lib2-f2': 0x21210,
     71     'T: F1': 0x50110, 'T: F2': 0x50210, 'T: F3': 0x50410 };
     72 
     73 
     74 ProfileTestDriver.prototype.addFunctions_ = function() {
     75   this.profile.addLibrary('lib1', 0x11000, 0x12000);
     76   this.profile.addStaticCode('lib1-f1', 0x11100, 0x11900);
     77   this.profile.addStaticCode('lib1-f2', 0x11200, 0x11500);
     78   this.profile.addLibrary('lib2', 0x21000, 0x22000);
     79   this.profile.addStaticCode('lib2-f1', 0x21100, 0x21900);
     80   this.profile.addStaticCode('lib2-f2', 0x21200, 0x21500);
     81   this.profile.addCode('T', 'F1', 0x50100, 0x100);
     82   this.profile.addCode('T', 'F2', 0x50200, 0x100);
     83   this.profile.addCode('T', 'F3', 0x50400, 0x100);
     84 };
     85 
     86 
     87 ProfileTestDriver.prototype.enter = function(funcName) {
     88   // Stack looks like this: [pc, caller, ..., main].
     89   // Therefore, we are adding entries at the beginning.
     90   this.stack_.unshift(this.funcAddrs_[funcName]);
     91   this.profile.recordTick(this.stack_);
     92 };
     93 
     94 
     95 ProfileTestDriver.prototype.stay = function() {
     96   this.profile.recordTick(this.stack_);
     97 };
     98 
     99 
    100 ProfileTestDriver.prototype.leave = function() {
    101   this.stack_.shift();
    102 };
    103 
    104 
    105 ProfileTestDriver.prototype.execute = function() {
    106   this.enter('lib1-f1');
    107     this.enter('lib1-f2');
    108       this.enter('T: F1');
    109         this.enter('T: F2');
    110         this.leave();
    111       this.stay();
    112         this.enter('lib2-f1');
    113           this.enter('lib2-f1');
    114           this.leave();
    115         this.stay();
    116         this.leave();
    117         this.enter('T: F3');
    118           this.enter('T: F3');
    119             this.enter('T: F3');
    120             this.leave();
    121             this.enter('T: F2');
    122             this.stay();
    123             this.leave();
    124           this.leave();
    125         this.leave();
    126       this.leave();
    127       this.enter('lib2-f1');
    128         this.enter('lib1-f1');
    129         this.leave();
    130       this.leave();
    131     this.stay();
    132   this.leave();
    133 };
    134 
    135 
    136 function Inherits(childCtor, parentCtor) {
    137   function tempCtor() {};
    138   tempCtor.prototype = parentCtor.prototype;
    139   childCtor.superClass_ = parentCtor.prototype;
    140   childCtor.prototype = new tempCtor();
    141   childCtor.prototype.constructor = childCtor;
    142 };
    143 
    144 
    145 (function testCallTreeBuilding() {
    146   function Driver() {
    147     ProfileTestDriver.call(this);
    148     this.namesTopDown = [];
    149     this.namesBottomUp = [];
    150   };
    151   Inherits(Driver, ProfileTestDriver);
    152 
    153   Driver.prototype.enter = function(func) {
    154     this.namesTopDown.push(func);
    155     this.namesBottomUp.unshift(func);
    156     assertNoPathExists(this.profile.getTopDownProfile().getRoot(), this.namesTopDown,
    157         'pre enter/topDown');
    158     assertNoPathExists(this.profile.getBottomUpProfile().getRoot(), this.namesBottomUp,
    159         'pre enter/bottomUp');
    160     Driver.superClass_.enter.call(this, func);
    161     assertPathExists(this.profile.getTopDownProfile().getRoot(), this.namesTopDown,
    162         'post enter/topDown');
    163     assertPathExists(this.profile.getBottomUpProfile().getRoot(), this.namesBottomUp,
    164         'post enter/bottomUp');
    165   };
    166 
    167   Driver.prototype.stay = function() {
    168     var preTopDownNodes = countNodes(this.profile, this.profile.traverseTopDownTree);
    169     var preBottomUpNodes = countNodes(this.profile, this.profile.traverseBottomUpTree);
    170     Driver.superClass_.stay.call(this);
    171     var postTopDownNodes = countNodes(this.profile, this.profile.traverseTopDownTree);
    172     var postBottomUpNodes = countNodes(this.profile, this.profile.traverseBottomUpTree);
    173     // Must be no changes in tree layout.
    174     assertEquals(preTopDownNodes, postTopDownNodes, 'stay/topDown');
    175     assertEquals(preBottomUpNodes, postBottomUpNodes, 'stay/bottomUp');
    176   };
    177 
    178   Driver.prototype.leave = function() {
    179     Driver.superClass_.leave.call(this);
    180     this.namesTopDown.pop();
    181     this.namesBottomUp.shift();
    182   };
    183 
    184   var testDriver = new Driver();
    185   testDriver.execute();
    186 })();
    187 
    188 
    189 function assertNodeWeights(root, path, selfTicks, totalTicks) {
    190   var node = root.descendToChild(path);
    191   var stack = stackToString(path);
    192   assertNotNull(node, 'node not found: ' + stack);
    193   assertEquals(selfTicks, node.selfWeight, 'self of ' + stack);
    194   assertEquals(totalTicks, node.totalWeight, 'total of ' + stack);
    195 };
    196 
    197 
    198 (function testTopDownRootProfileTicks() {
    199   var testDriver = new ProfileTestDriver();
    200   testDriver.execute();
    201 
    202   var pathWeights = [
    203     [['lib1-f1'], 1, 16],
    204     [['lib1-f1', 'lib1-f2'], 2, 15],
    205     [['lib1-f1', 'lib1-f2', 'T: F1'], 2, 11],
    206     [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F2'], 1, 1],
    207     [['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1'], 2, 3],
    208     [['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1', 'lib2-f1'], 1, 1],
    209     [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3'], 1, 5],
    210     [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3'], 1, 4],
    211     [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F3'], 1, 1],
    212     [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F2'], 2, 2],
    213     [['lib1-f1', 'lib1-f2', 'lib2-f1'], 1, 2],
    214     [['lib1-f1', 'lib1-f2', 'lib2-f1', 'lib1-f1'], 1, 1]
    215   ];
    216 
    217   var root = testDriver.profile.getTopDownProfile().getRoot();
    218   for (var i = 0; i < pathWeights.length; ++i) {
    219     var data = pathWeights[i];
    220     assertNodeWeights(root, data[0], data[1], data[2]);
    221   }
    222 })();
    223 
    224 
    225 (function testRootFlatProfileTicks() {
    226   function Driver() {
    227     ProfileTestDriver.call(this);
    228     this.namesTopDown = [''];
    229     this.counters = {};
    230     this.root = null;
    231   };
    232   Inherits(Driver, ProfileTestDriver);
    233 
    234   Driver.prototype.increment = function(func, self, total) {
    235     if (!(func in this.counters)) {
    236       this.counters[func] = { self: 0, total: 0 };
    237     }
    238     this.counters[func].self += self;
    239     this.counters[func].total += total;
    240   };
    241 
    242   Driver.prototype.incrementTotals = function() {
    243     // Only count each function in the stack once.
    244     var met = {};
    245     for (var i = 0; i < this.namesTopDown.length; ++i) {
    246       var name = this.namesTopDown[i];
    247       if (!(name in met)) {
    248         this.increment(name, 0, 1);
    249       }
    250       met[name] = true;
    251     }
    252   };
    253 
    254   Driver.prototype.enter = function(func) {
    255     Driver.superClass_.enter.call(this, func);
    256     this.namesTopDown.push(func);
    257     this.increment(func, 1, 0);
    258     this.incrementTotals();
    259   };
    260 
    261   Driver.prototype.stay = function() {
    262     Driver.superClass_.stay.call(this);
    263     this.increment(this.namesTopDown[this.namesTopDown.length - 1], 1, 0);
    264     this.incrementTotals();
    265   };
    266 
    267   Driver.prototype.leave = function() {
    268     Driver.superClass_.leave.call(this);
    269     this.namesTopDown.pop();
    270   };
    271 
    272   Driver.prototype.extractRoot = function() {
    273     assertTrue('' in this.counters);
    274     this.root = this.counters[''];
    275     delete this.counters[''];
    276   };
    277 
    278   var testDriver = new Driver();
    279   testDriver.execute();
    280   testDriver.extractRoot();
    281 
    282   var counted = 0;
    283   for (var c in testDriver.counters) {
    284     counted++;
    285   }
    286 
    287   var flatProfileRoot = testDriver.profile.getFlatProfile().getRoot();
    288   assertEquals(testDriver.root.self, flatProfileRoot.selfWeight);
    289   assertEquals(testDriver.root.total, flatProfileRoot.totalWeight);
    290 
    291   var flatProfile = flatProfileRoot.exportChildren();
    292   assertEquals(counted, flatProfile.length, 'counted vs. flatProfile');
    293   for (var i = 0; i < flatProfile.length; ++i) {
    294     var rec = flatProfile[i];
    295     assertTrue(rec.label in testDriver.counters, 'uncounted: ' + rec.label);
    296     var reference = testDriver.counters[rec.label];
    297     assertEquals(reference.self, rec.selfWeight, 'self of ' + rec.label);
    298     assertEquals(reference.total, rec.totalWeight, 'total of ' + rec.label);
    299   }
    300 
    301 })();
    302 
    303 
    304 (function testFunctionCalleesProfileTicks() {
    305   var testDriver = new ProfileTestDriver();
    306   testDriver.execute();
    307 
    308   var pathWeights = [
    309     [['lib2-f1'], 3, 5],
    310     [['lib2-f1', 'lib2-f1'], 1, 1],
    311     [['lib2-f1', 'lib1-f1'], 1, 1]
    312   ];
    313 
    314   var profile = testDriver.profile.getTopDownProfile('lib2-f1');
    315   var root = profile.getRoot();
    316   for (var i = 0; i < pathWeights.length; ++i) {
    317     var data = pathWeights[i];
    318     assertNodeWeights(root, data[0], data[1], data[2]);
    319   }
    320 })();
    321 
    322 
    323 (function testFunctionFlatProfileTicks() {
    324   var testDriver = new ProfileTestDriver();
    325   testDriver.execute();
    326 
    327   var flatWeights = {
    328     'lib2-f1': [1, 1],
    329     'lib1-f1': [1, 1]
    330   };
    331 
    332   var flatProfileRoot =
    333      testDriver.profile.getFlatProfile('lib2-f1').findOrAddChild('lib2-f1');
    334   assertEquals(3, flatProfileRoot.selfWeight);
    335   assertEquals(5, flatProfileRoot.totalWeight);
    336 
    337   var flatProfile = flatProfileRoot.exportChildren();
    338   assertEquals(2, flatProfile.length, 'counted vs. flatProfile');
    339   for (var i = 0; i < flatProfile.length; ++i) {
    340     var rec = flatProfile[i];
    341     assertTrue(rec.label in flatWeights, 'uncounted: ' + rec.label);
    342     var reference = flatWeights[rec.label];
    343     assertEquals(reference[0], rec.selfWeight, 'self of ' + rec.label);
    344     assertEquals(reference[1], rec.totalWeight, 'total of ' + rec.label);
    345   }
    346 
    347 })();
    348 
    349