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