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 
     29 // Initlialize namespaces
     30 var devtools = devtools || {};
     31 devtools.profiler = devtools.profiler || {};
     32 
     33 
     34 /**
     35  * Constructs a mapper that maps addresses into code entries.
     36  *
     37  * @constructor
     38  */
     39 devtools.profiler.CodeMap = function() {
     40   /**
     41    * Dynamic code entries. Used for JIT compiled code.
     42    */
     43   this.dynamics_ = new goog.structs.SplayTree();
     44 
     45   /**
     46    * Name generator for entries having duplicate names.
     47    */
     48   this.dynamicsNameGen_ = new devtools.profiler.CodeMap.NameGenerator();
     49 
     50   /**
     51    * Static code entries. Used for statically compiled code.
     52    */
     53   this.statics_ = new goog.structs.SplayTree();
     54 
     55   /**
     56    * Libraries entries. Used for the whole static code libraries.
     57    */
     58   this.libraries_ = new goog.structs.SplayTree();
     59 
     60   /**
     61    * Map of memory pages occupied with static code.
     62    */
     63   this.pages_ = [];
     64 };
     65 
     66 
     67 /**
     68  * The number of alignment bits in a page address.
     69  */
     70 devtools.profiler.CodeMap.PAGE_ALIGNMENT = 12;
     71 
     72 
     73 /**
     74  * Page size in bytes.
     75  */
     76 devtools.profiler.CodeMap.PAGE_SIZE =
     77     1 << devtools.profiler.CodeMap.PAGE_ALIGNMENT;
     78 
     79 
     80 /**
     81  * Adds a dynamic (i.e. moveable and discardable) code entry.
     82  *
     83  * @param {number} start The starting address.
     84  * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object.
     85  */
     86 devtools.profiler.CodeMap.prototype.addCode = function(start, codeEntry) {
     87   this.dynamics_.insert(start, codeEntry);
     88 };
     89 
     90 
     91 /**
     92  * Moves a dynamic code entry. Throws an exception if there is no dynamic
     93  * code entry with the specified starting address.
     94  *
     95  * @param {number} from The starting address of the entry being moved.
     96  * @param {number} to The destination address.
     97  */
     98 devtools.profiler.CodeMap.prototype.moveCode = function(from, to) {
     99   var removedNode = this.dynamics_.remove(from);
    100   this.dynamics_.insert(to, removedNode.value);
    101 };
    102 
    103 
    104 /**
    105  * Discards a dynamic code entry. Throws an exception if there is no dynamic
    106  * code entry with the specified starting address.
    107  *
    108  * @param {number} start The starting address of the entry being deleted.
    109  */
    110 devtools.profiler.CodeMap.prototype.deleteCode = function(start) {
    111   var removedNode = this.dynamics_.remove(start);
    112 };
    113 
    114 
    115 /**
    116  * Adds a library entry.
    117  *
    118  * @param {number} start The starting address.
    119  * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object.
    120  */
    121 devtools.profiler.CodeMap.prototype.addLibrary = function(
    122     start, codeEntry) {
    123   this.markPages_(start, start + codeEntry.size);
    124   this.libraries_.insert(start, codeEntry);
    125 };
    126 
    127 
    128 /**
    129  * Adds a static code entry.
    130  *
    131  * @param {number} start The starting address.
    132  * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object.
    133  */
    134 devtools.profiler.CodeMap.prototype.addStaticCode = function(
    135     start, codeEntry) {
    136   this.statics_.insert(start, codeEntry);
    137 };
    138 
    139 
    140 /**
    141  * @private
    142  */
    143 devtools.profiler.CodeMap.prototype.markPages_ = function(start, end) {
    144   for (var addr = start; addr <= end;
    145        addr += devtools.profiler.CodeMap.PAGE_SIZE) {
    146     this.pages_[addr >>> devtools.profiler.CodeMap.PAGE_ALIGNMENT] = 1;
    147   }
    148 };
    149 
    150 
    151 /**
    152  * @private
    153  */
    154 devtools.profiler.CodeMap.prototype.isAddressBelongsTo_ = function(addr, node) {
    155   return addr >= node.key && addr < (node.key + node.value.size);
    156 };
    157 
    158 
    159 /**
    160  * @private
    161  */
    162 devtools.profiler.CodeMap.prototype.findInTree_ = function(tree, addr) {
    163   var node = tree.findGreatestLessThan(addr);
    164   return node && this.isAddressBelongsTo_(addr, node) ? node.value : null;
    165 };
    166 
    167 
    168 /**
    169  * Finds a code entry that contains the specified address. Both static and
    170  * dynamic code entries are considered.
    171  *
    172  * @param {number} addr Address.
    173  */
    174 devtools.profiler.CodeMap.prototype.findEntry = function(addr) {
    175   var pageAddr = addr >>> devtools.profiler.CodeMap.PAGE_ALIGNMENT;
    176   if (pageAddr in this.pages_) {
    177     // Static code entries can contain "holes" of unnamed code.
    178     // In this case, the whole library is assigned to this address.
    179     return this.findInTree_(this.statics_, addr) ||
    180         this.findInTree_(this.libraries_, addr);
    181   }
    182   var min = this.dynamics_.findMin();
    183   var max = this.dynamics_.findMax();
    184   if (max != null && addr < (max.key + max.value.size) && addr >= min.key) {
    185     var dynaEntry = this.findInTree_(this.dynamics_, addr);
    186     if (dynaEntry == null) return null;
    187     // Dedupe entry name.
    188     if (!dynaEntry.nameUpdated_) {
    189       dynaEntry.name = this.dynamicsNameGen_.getName(dynaEntry.name);
    190       dynaEntry.nameUpdated_ = true;
    191     }
    192     return dynaEntry;
    193   }
    194   return null;
    195 };
    196 
    197 
    198 /**
    199  * Returns a dynamic code entry using its starting address.
    200  *
    201  * @param {number} addr Address.
    202  */
    203 devtools.profiler.CodeMap.prototype.findDynamicEntryByStartAddress =
    204     function(addr) {
    205   var node = this.dynamics_.find(addr);
    206   return node ? node.value : null;
    207 };
    208 
    209 
    210 /**
    211  * Returns an array of all dynamic code entries.
    212  */
    213 devtools.profiler.CodeMap.prototype.getAllDynamicEntries = function() {
    214   return this.dynamics_.exportValues();
    215 };
    216 
    217 
    218 /**
    219  * Returns an array of all static code entries.
    220  */
    221 devtools.profiler.CodeMap.prototype.getAllStaticEntries = function() {
    222   return this.statics_.exportValues();
    223 };
    224 
    225 
    226 /**
    227  * Returns an array of all libraries entries.
    228  */
    229 devtools.profiler.CodeMap.prototype.getAllLibrariesEntries = function() {
    230   return this.libraries_.exportValues();
    231 };
    232 
    233 
    234 /**
    235  * Creates a code entry object.
    236  *
    237  * @param {number} size Code entry size in bytes.
    238  * @param {string} opt_name Code entry name.
    239  * @constructor
    240  */
    241 devtools.profiler.CodeMap.CodeEntry = function(size, opt_name) {
    242   this.size = size;
    243   this.name = opt_name || '';
    244   this.nameUpdated_ = false;
    245 };
    246 
    247 
    248 devtools.profiler.CodeMap.CodeEntry.prototype.getName = function() {
    249   return this.name;
    250 };
    251 
    252 
    253 devtools.profiler.CodeMap.CodeEntry.prototype.toString = function() {
    254   return this.name + ': ' + this.size.toString(16);
    255 };
    256 
    257 
    258 devtools.profiler.CodeMap.NameGenerator = function() {
    259   this.knownNames_ = {};
    260 };
    261 
    262 
    263 devtools.profiler.CodeMap.NameGenerator.prototype.getName = function(name) {
    264   if (!(name in this.knownNames_)) {
    265     this.knownNames_[name] = 0;
    266     return name;
    267   }
    268   var count = ++this.knownNames_[name];
    269   return name + ' {' + count + '}';
    270 };
    271