tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HeapAnalyses.worker.js (9399B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // This is a worker which reads offline heap snapshots into memory and performs
      6 // heavyweight analyses on them without blocking the main thread. A
      7 // HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
      8 // instance. See HeapAnalysesClient.js.
      9 
     10 "use strict";
     11 
     12 /* import-globals-from /toolkit/components/workerloader/require.js */
     13 importScripts("resource://gre/modules/workers/require.js");
     14 /* import-globals-from ../worker/helper.js */
     15 importScripts("resource://devtools/shared/worker/helper.js");
     16 const {
     17  censusReportToCensusTreeNode,
     18 } = require("resource://devtools/shared/heapsnapshot/census-tree-node.js");
     19 const DominatorTreeNode = require("resource://devtools/shared/heapsnapshot/DominatorTreeNode.js");
     20 const CensusUtils = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
     21 
     22 const DEFAULT_START_INDEX = 0;
     23 const DEFAULT_MAX_COUNT = 50;
     24 
     25 /**
     26 * The set of HeapSnapshot instances this worker has read into memory. Keyed by
     27 * snapshot file path.
     28 */
     29 const snapshots = Object.create(null);
     30 
     31 /**
     32 * The set of `DominatorTree`s that have been computed, mapped by their id (aka
     33 * the index into this array).
     34 *
     35 * @see /dom/webidl/DominatorTree.webidl
     36 */
     37 const dominatorTrees = [];
     38 
     39 /**
     40 * The i^th HeapSnapshot in this array is the snapshot used to generate the i^th
     41 * dominator tree in `dominatorTrees` above. This lets us map from a dominator
     42 * tree id to the snapshot it came from.
     43 */
     44 const dominatorTreeSnapshots = [];
     45 
     46 /**
     47 * @see HeapAnalysesClient.prototype.readHeapSnapshot
     48 */
     49 workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
     50  snapshots[snapshotFilePath] = ChromeUtils.readHeapSnapshot(snapshotFilePath);
     51  return true;
     52 });
     53 
     54 /**
     55 * @see HeapAnalysesClient.prototype.deleteHeapSnapshot
     56 */
     57 workerHelper.createTask(self, "deleteHeapSnapshot", ({ snapshotFilePath }) => {
     58  const snapshot = snapshots[snapshotFilePath];
     59  if (!snapshot) {
     60    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
     61  }
     62 
     63  snapshots[snapshotFilePath] = undefined;
     64 
     65  const dominatorTreeId = dominatorTreeSnapshots.indexOf(snapshot);
     66  if (dominatorTreeId != -1) {
     67    dominatorTreeSnapshots[dominatorTreeId] = undefined;
     68    dominatorTrees[dominatorTreeId] = undefined;
     69  }
     70 });
     71 
     72 /**
     73 * @see HeapAnalysesClient.prototype.takeCensus
     74 */
     75 workerHelper.createTask(
     76  self,
     77  "takeCensus",
     78  ({ snapshotFilePath, censusOptions, requestOptions }) => {
     79    if (!snapshots[snapshotFilePath]) {
     80      throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
     81    }
     82 
     83    let report = snapshots[snapshotFilePath].takeCensus(censusOptions);
     84    let parentMap;
     85 
     86    if (requestOptions.asTreeNode || requestOptions.asInvertedTreeNode) {
     87      const opts = { filter: requestOptions.filter || null };
     88      if (requestOptions.asInvertedTreeNode) {
     89        opts.invert = true;
     90      }
     91      report = censusReportToCensusTreeNode(
     92        censusOptions.breakdown,
     93        report,
     94        opts
     95      );
     96      parentMap = CensusUtils.createParentMap(report);
     97    }
     98 
     99    return { report, parentMap };
    100  }
    101 );
    102 
    103 /**
    104 * @see HeapAnalysesClient.prototype.getCensusIndividuals
    105 */
    106 workerHelper.createTask(self, "getCensusIndividuals", request => {
    107  const {
    108    dominatorTreeId,
    109    indices,
    110    censusBreakdown,
    111    labelBreakdown,
    112    maxRetainingPaths,
    113    maxIndividuals,
    114  } = request;
    115 
    116  const dominatorTree = dominatorTrees[dominatorTreeId];
    117  if (!dominatorTree) {
    118    throw new Error(
    119      `There does not exist a DominatorTree with the id ${dominatorTreeId}`
    120    );
    121  }
    122 
    123  const snapshot = dominatorTreeSnapshots[dominatorTreeId];
    124  const nodeIds = CensusUtils.getCensusIndividuals(
    125    indices,
    126    censusBreakdown,
    127    snapshot
    128  );
    129 
    130  const nodes = nodeIds
    131    .sort(
    132      (a, b) =>
    133        dominatorTree.getRetainedSize(b) - dominatorTree.getRetainedSize(a)
    134    )
    135    .slice(0, maxIndividuals)
    136    .map(id => {
    137      const { label, shallowSize } = DominatorTreeNode.getLabelAndShallowSize(
    138        id,
    139        snapshot,
    140        labelBreakdown
    141      );
    142      const retainedSize = dominatorTree.getRetainedSize(id);
    143      const node = new DominatorTreeNode(id, label, shallowSize, retainedSize);
    144      node.moreChildrenAvailable = false;
    145      return node;
    146    });
    147 
    148  DominatorTreeNode.attachShortestPaths(
    149    snapshot,
    150    labelBreakdown,
    151    dominatorTree.root,
    152    nodes,
    153    maxRetainingPaths
    154  );
    155 
    156  return { nodes };
    157 });
    158 
    159 /**
    160 * @see HeapAnalysesClient.prototype.takeCensusDiff
    161 */
    162 workerHelper.createTask(self, "takeCensusDiff", request => {
    163  const {
    164    firstSnapshotFilePath,
    165    secondSnapshotFilePath,
    166    censusOptions,
    167    requestOptions,
    168  } = request;
    169 
    170  if (!snapshots[firstSnapshotFilePath]) {
    171    throw new Error(`No known heap snapshot for '${firstSnapshotFilePath}'`);
    172  }
    173 
    174  if (!snapshots[secondSnapshotFilePath]) {
    175    throw new Error(`No known heap snapshot for '${secondSnapshotFilePath}'`);
    176  }
    177 
    178  const first = snapshots[firstSnapshotFilePath].takeCensus(censusOptions);
    179  const second = snapshots[secondSnapshotFilePath].takeCensus(censusOptions);
    180  let delta = CensusUtils.diff(censusOptions.breakdown, first, second);
    181  let parentMap;
    182 
    183  if (requestOptions.asTreeNode || requestOptions.asInvertedTreeNode) {
    184    const opts = { filter: requestOptions.filter || null };
    185    if (requestOptions.asInvertedTreeNode) {
    186      opts.invert = true;
    187    }
    188    delta = censusReportToCensusTreeNode(censusOptions.breakdown, delta, opts);
    189    parentMap = CensusUtils.createParentMap(delta);
    190  }
    191 
    192  return { delta, parentMap };
    193 });
    194 
    195 /**
    196 * @see HeapAnalysesClient.prototype.getCreationTime
    197 */
    198 workerHelper.createTask(self, "getCreationTime", snapshotFilePath => {
    199  if (!snapshots[snapshotFilePath]) {
    200    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
    201  }
    202  return snapshots[snapshotFilePath].creationTime;
    203 });
    204 
    205 /**
    206 * @see HeapAnalysesClient.prototype.computeDominatorTree
    207 */
    208 workerHelper.createTask(self, "computeDominatorTree", snapshotFilePath => {
    209  const snapshot = snapshots[snapshotFilePath];
    210  if (!snapshot) {
    211    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
    212  }
    213 
    214  const id = dominatorTrees.length;
    215  dominatorTrees.push(snapshot.computeDominatorTree());
    216  dominatorTreeSnapshots.push(snapshot);
    217  return id;
    218 });
    219 
    220 /**
    221 * @see HeapAnalysesClient.prototype.getDominatorTree
    222 */
    223 workerHelper.createTask(self, "getDominatorTree", request => {
    224  const {
    225    dominatorTreeId,
    226    breakdown,
    227    maxDepth,
    228    maxSiblings,
    229    maxRetainingPaths,
    230  } = request;
    231 
    232  if (!(dominatorTreeId >= 0 && dominatorTreeId < dominatorTrees.length)) {
    233    throw new Error(
    234      `There does not exist a DominatorTree with the id ${dominatorTreeId}`
    235    );
    236  }
    237 
    238  const dominatorTree = dominatorTrees[dominatorTreeId];
    239  const snapshot = dominatorTreeSnapshots[dominatorTreeId];
    240 
    241  const tree = DominatorTreeNode.partialTraversal(
    242    dominatorTree,
    243    snapshot,
    244    breakdown,
    245    maxDepth,
    246    maxSiblings
    247  );
    248 
    249  const nodes = [];
    250  (function getNodes(node) {
    251    nodes.push(node);
    252    if (node.children) {
    253      for (let i = 0, length = node.children.length; i < length; i++) {
    254        getNodes(node.children[i]);
    255      }
    256    }
    257  })(tree);
    258 
    259  DominatorTreeNode.attachShortestPaths(
    260    snapshot,
    261    breakdown,
    262    dominatorTree.root,
    263    nodes,
    264    maxRetainingPaths
    265  );
    266 
    267  return tree;
    268 });
    269 
    270 /**
    271 * @see HeapAnalysesClient.prototype.getImmediatelyDominated
    272 */
    273 workerHelper.createTask(self, "getImmediatelyDominated", request => {
    274  const {
    275    dominatorTreeId,
    276    nodeId,
    277    breakdown,
    278    startIndex,
    279    maxCount,
    280    maxRetainingPaths,
    281  } = request;
    282 
    283  if (!(dominatorTreeId >= 0 && dominatorTreeId < dominatorTrees.length)) {
    284    throw new Error(
    285      `There does not exist a DominatorTree with the id ${dominatorTreeId}`
    286    );
    287  }
    288 
    289  const dominatorTree = dominatorTrees[dominatorTreeId];
    290  const snapshot = dominatorTreeSnapshots[dominatorTreeId];
    291 
    292  const childIds = dominatorTree.getImmediatelyDominated(nodeId);
    293  if (!childIds) {
    294    throw new Error(`${nodeId} is not a node id in the dominator tree`);
    295  }
    296 
    297  const start = startIndex || DEFAULT_START_INDEX;
    298  const count = maxCount || DEFAULT_MAX_COUNT;
    299  const end = start + count;
    300 
    301  const nodes = childIds.slice(start, end).map(id => {
    302    const { label, shallowSize } = DominatorTreeNode.getLabelAndShallowSize(
    303      id,
    304      snapshot,
    305      breakdown
    306    );
    307    const retainedSize = dominatorTree.getRetainedSize(id);
    308    const node = new DominatorTreeNode(id, label, shallowSize, retainedSize);
    309    node.parentId = nodeId;
    310    // DominatorTree.getImmediatelyDominated will always return non-null here
    311    // because we got the id directly from the dominator tree.
    312    node.moreChildrenAvailable =
    313      !!dominatorTree.getImmediatelyDominated(id).length;
    314    return node;
    315  });
    316 
    317  const path = [];
    318  let id = nodeId;
    319  do {
    320    path.push(id);
    321    id = dominatorTree.getImmediateDominator(id);
    322  } while (id !== null);
    323  path.reverse();
    324 
    325  const moreChildrenAvailable = childIds.length > end;
    326 
    327  DominatorTreeNode.attachShortestPaths(
    328    snapshot,
    329    breakdown,
    330    dominatorTree.root,
    331    nodes,
    332    maxRetainingPaths
    333  );
    334 
    335  return { nodes, moreChildrenAvailable, path };
    336 });