• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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