mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
Follow-up to 4e2e9be6a - Move HeapSnapshot DevTools-only Modules back to DevTools
I am so done with this. Resolves #316
This commit is contained in:
parent
f1182eaec9
commit
021bff6bf8
10 changed files with 16 additions and 11 deletions
303
devtools/shared/heapsnapshot/HeapAnalysesWorker.js
Normal file
303
devtools/shared/heapsnapshot/HeapAnalysesWorker.js
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* global ThreadSafeChromeUtils*/
|
||||
|
||||
// This is a worker which reads offline heap snapshots into memory and performs
|
||||
// heavyweight analyses on them without blocking the main thread. A
|
||||
// HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
|
||||
// instance. See HeapAnalysesClient.js.
|
||||
|
||||
"use strict";
|
||||
|
||||
importScripts("resource://gre/modules/workers/require.js");
|
||||
importScripts("resource://devtools/shared/worker/helper.js");
|
||||
const { censusReportToCensusTreeNode } = require("resource://devtools/shared/heapsnapshot/census-tree-node.js");
|
||||
const DominatorTreeNode = require("resource://devtools/shared/heapsnapshot/DominatorTreeNode.js");
|
||||
const CensusUtils = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
|
||||
|
||||
const DEFAULT_START_INDEX = 0;
|
||||
const DEFAULT_MAX_COUNT = 50;
|
||||
|
||||
/**
|
||||
* The set of HeapSnapshot instances this worker has read into memory. Keyed by
|
||||
* snapshot file path.
|
||||
*/
|
||||
const snapshots = Object.create(null);
|
||||
|
||||
/**
|
||||
* The set of `DominatorTree`s that have been computed, mapped by their id (aka
|
||||
* the index into this array).
|
||||
*
|
||||
* @see /dom/webidl/DominatorTree.webidl
|
||||
*/
|
||||
const dominatorTrees = [];
|
||||
|
||||
/**
|
||||
* The i^th HeapSnapshot in this array is the snapshot used to generate the i^th
|
||||
* dominator tree in `dominatorTrees` above. This lets us map from a dominator
|
||||
* tree id to the snapshot it came from.
|
||||
*/
|
||||
const dominatorTreeSnapshots = [];
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.readHeapSnapshot
|
||||
*/
|
||||
workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
|
||||
snapshots[snapshotFilePath] =
|
||||
ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.deleteHeapSnapshot
|
||||
*/
|
||||
workerHelper.createTask(self, "deleteHeapSnapshot", ({ snapshotFilePath }) => {
|
||||
let snapshot = snapshots[snapshotFilePath];
|
||||
if (!snapshot) {
|
||||
throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
|
||||
}
|
||||
|
||||
snapshots[snapshotFilePath] = undefined;
|
||||
|
||||
let dominatorTreeId = dominatorTreeSnapshots.indexOf(snapshot);
|
||||
if (dominatorTreeId != -1) {
|
||||
dominatorTreeSnapshots[dominatorTreeId] = undefined;
|
||||
dominatorTrees[dominatorTreeId] = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.takeCensus
|
||||
*/
|
||||
workerHelper.createTask(self, "takeCensus", ({ snapshotFilePath, censusOptions, requestOptions }) => {
|
||||
if (!snapshots[snapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
|
||||
}
|
||||
|
||||
let report = snapshots[snapshotFilePath].takeCensus(censusOptions);
|
||||
let parentMap;
|
||||
|
||||
if (requestOptions.asTreeNode || requestOptions.asInvertedTreeNode) {
|
||||
const opts = { filter: requestOptions.filter || null };
|
||||
if (requestOptions.asInvertedTreeNode) {
|
||||
opts.invert = true;
|
||||
}
|
||||
report = censusReportToCensusTreeNode(censusOptions.breakdown, report, opts);
|
||||
parentMap = CensusUtils.createParentMap(report);
|
||||
}
|
||||
|
||||
return { report, parentMap };
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.getCensusIndividuals
|
||||
*/
|
||||
workerHelper.createTask(self, "getCensusIndividuals", request => {
|
||||
const {
|
||||
dominatorTreeId,
|
||||
indices,
|
||||
censusBreakdown,
|
||||
labelBreakdown,
|
||||
maxRetainingPaths,
|
||||
maxIndividuals,
|
||||
} = request;
|
||||
|
||||
const dominatorTree = dominatorTrees[dominatorTreeId];
|
||||
if (!dominatorTree) {
|
||||
throw new Error(
|
||||
`There does not exist a DominatorTree with the id ${dominatorTreeId}`);
|
||||
}
|
||||
|
||||
const snapshot = dominatorTreeSnapshots[dominatorTreeId];
|
||||
const nodeIds = CensusUtils.getCensusIndividuals(indices, censusBreakdown, snapshot);
|
||||
|
||||
const nodes = nodeIds
|
||||
.sort((a, b) => dominatorTree.getRetainedSize(b) - dominatorTree.getRetainedSize(a))
|
||||
.slice(0, maxIndividuals)
|
||||
.map(id => {
|
||||
const { label, shallowSize } =
|
||||
DominatorTreeNode.getLabelAndShallowSize(id, snapshot, labelBreakdown);
|
||||
const retainedSize = dominatorTree.getRetainedSize(id);
|
||||
const node = new DominatorTreeNode(id, label, shallowSize, retainedSize);
|
||||
node.moreChildrenAvailable = false;
|
||||
return node;
|
||||
});
|
||||
|
||||
DominatorTreeNode.attachShortestPaths(snapshot,
|
||||
labelBreakdown,
|
||||
dominatorTree.root,
|
||||
nodes,
|
||||
maxRetainingPaths);
|
||||
|
||||
return { nodes };
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.takeCensusDiff
|
||||
*/
|
||||
workerHelper.createTask(self, "takeCensusDiff", request => {
|
||||
const {
|
||||
firstSnapshotFilePath,
|
||||
secondSnapshotFilePath,
|
||||
censusOptions,
|
||||
requestOptions
|
||||
} = request;
|
||||
|
||||
if (!snapshots[firstSnapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${firstSnapshotFilePath}'`);
|
||||
}
|
||||
|
||||
if (!snapshots[secondSnapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${secondSnapshotFilePath}'`);
|
||||
}
|
||||
|
||||
const first = snapshots[firstSnapshotFilePath].takeCensus(censusOptions);
|
||||
const second = snapshots[secondSnapshotFilePath].takeCensus(censusOptions);
|
||||
let delta = CensusUtils.diff(censusOptions.breakdown, first, second);
|
||||
let parentMap;
|
||||
|
||||
if (requestOptions.asTreeNode || requestOptions.asInvertedTreeNode) {
|
||||
const opts = { filter: requestOptions.filter || null };
|
||||
if (requestOptions.asInvertedTreeNode) {
|
||||
opts.invert = true;
|
||||
}
|
||||
delta = censusReportToCensusTreeNode(censusOptions.breakdown, delta, opts);
|
||||
parentMap = CensusUtils.createParentMap(delta);
|
||||
}
|
||||
|
||||
return { delta, parentMap };
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.getCreationTime
|
||||
*/
|
||||
workerHelper.createTask(self, "getCreationTime", snapshotFilePath => {
|
||||
if (!snapshots[snapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
|
||||
}
|
||||
return snapshots[snapshotFilePath].creationTime;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.computeDominatorTree
|
||||
*/
|
||||
workerHelper.createTask(self, "computeDominatorTree", snapshotFilePath => {
|
||||
const snapshot = snapshots[snapshotFilePath];
|
||||
if (!snapshot) {
|
||||
throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
|
||||
}
|
||||
|
||||
const id = dominatorTrees.length;
|
||||
dominatorTrees.push(snapshot.computeDominatorTree());
|
||||
dominatorTreeSnapshots.push(snapshot);
|
||||
return id;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.getDominatorTree
|
||||
*/
|
||||
workerHelper.createTask(self, "getDominatorTree", request => {
|
||||
const {
|
||||
dominatorTreeId,
|
||||
breakdown,
|
||||
maxDepth,
|
||||
maxSiblings,
|
||||
maxRetainingPaths,
|
||||
} = request;
|
||||
|
||||
if (!(0 <= dominatorTreeId && dominatorTreeId < dominatorTrees.length)) {
|
||||
throw new Error(
|
||||
`There does not exist a DominatorTree with the id ${dominatorTreeId}`);
|
||||
}
|
||||
|
||||
const dominatorTree = dominatorTrees[dominatorTreeId];
|
||||
const snapshot = dominatorTreeSnapshots[dominatorTreeId];
|
||||
|
||||
const tree = DominatorTreeNode.partialTraversal(dominatorTree,
|
||||
snapshot,
|
||||
breakdown,
|
||||
maxDepth,
|
||||
maxSiblings);
|
||||
|
||||
const nodes = [];
|
||||
(function getNodes(node) {
|
||||
nodes.push(node);
|
||||
if (node.children) {
|
||||
for (let i = 0, length = node.children.length; i < length; i++) {
|
||||
getNodes(node.children[i]);
|
||||
}
|
||||
}
|
||||
}(tree));
|
||||
|
||||
DominatorTreeNode.attachShortestPaths(snapshot,
|
||||
breakdown,
|
||||
dominatorTree.root,
|
||||
nodes,
|
||||
maxRetainingPaths);
|
||||
|
||||
return tree;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.getImmediatelyDominated
|
||||
*/
|
||||
workerHelper.createTask(self, "getImmediatelyDominated", request => {
|
||||
const {
|
||||
dominatorTreeId,
|
||||
nodeId,
|
||||
breakdown,
|
||||
startIndex,
|
||||
maxCount,
|
||||
maxRetainingPaths,
|
||||
} = request;
|
||||
|
||||
if (!(0 <= dominatorTreeId && dominatorTreeId < dominatorTrees.length)) {
|
||||
throw new Error(
|
||||
`There does not exist a DominatorTree with the id ${dominatorTreeId}`);
|
||||
}
|
||||
|
||||
const dominatorTree = dominatorTrees[dominatorTreeId];
|
||||
const snapshot = dominatorTreeSnapshots[dominatorTreeId];
|
||||
|
||||
const childIds = dominatorTree.getImmediatelyDominated(nodeId);
|
||||
if (!childIds) {
|
||||
throw new Error(`${nodeId} is not a node id in the dominator tree`);
|
||||
}
|
||||
|
||||
const start = startIndex || DEFAULT_START_INDEX;
|
||||
const count = maxCount || DEFAULT_MAX_COUNT;
|
||||
const end = start + count;
|
||||
|
||||
const nodes = childIds
|
||||
.slice(start, end)
|
||||
.map(id => {
|
||||
const { label, shallowSize } =
|
||||
DominatorTreeNode.getLabelAndShallowSize(id, snapshot, breakdown);
|
||||
const retainedSize = dominatorTree.getRetainedSize(id);
|
||||
const node = new DominatorTreeNode(id, label, shallowSize, retainedSize);
|
||||
node.parentId = nodeId;
|
||||
// DominatorTree.getImmediatelyDominated will always return non-null here
|
||||
// because we got the id directly from the dominator tree.
|
||||
node.moreChildrenAvailable = dominatorTree.getImmediatelyDominated(id).length > 0;
|
||||
return node;
|
||||
});
|
||||
|
||||
const path = [];
|
||||
let id = nodeId;
|
||||
do {
|
||||
path.push(id);
|
||||
id = dominatorTree.getImmediateDominator(id);
|
||||
} while (id !== null);
|
||||
path.reverse();
|
||||
|
||||
const moreChildrenAvailable = childIds.length > end;
|
||||
|
||||
DominatorTreeNode.attachShortestPaths(snapshot,
|
||||
breakdown,
|
||||
dominatorTree.root,
|
||||
nodes,
|
||||
maxRetainingPaths);
|
||||
|
||||
return { nodes, moreChildrenAvailable, path };
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue