summaryrefslogtreecommitdiffstats
path: root/devtools/client/memory/actions/diffing.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/memory/actions/diffing.js')
-rw-r--r--devtools/client/memory/actions/diffing.js221
1 files changed, 221 insertions, 0 deletions
diff --git a/devtools/client/memory/actions/diffing.js b/devtools/client/memory/actions/diffing.js
new file mode 100644
index 0000000000..1442cbc0a7
--- /dev/null
+++ b/devtools/client/memory/actions/diffing.js
@@ -0,0 +1,221 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ assert,
+ reportException,
+} = require("resource://devtools/shared/DevToolsUtils.js");
+const {
+ actions,
+ diffingState,
+ viewState,
+} = require("resource://devtools/client/memory/constants.js");
+const {
+ getSnapshot,
+ censusIsUpToDate,
+ snapshotIsDiffable,
+ findSelectedSnapshot,
+} = require("resource://devtools/client/memory/utils.js");
+
+/**
+ * Toggle diffing mode on or off.
+ */
+exports.toggleDiffing = function () {
+ return function ({ dispatch, getState }) {
+ dispatch({
+ type: actions.CHANGE_VIEW,
+ newViewState: getState().diffing ? viewState.CENSUS : viewState.DIFFING,
+ oldDiffing: getState().diffing,
+ oldSelected: findSelectedSnapshot(getState()),
+ });
+ };
+};
+
+/**
+ * Select the given snapshot for diffing.
+ *
+ * @param {snapshotModel} snapshot
+ */
+const selectSnapshotForDiffing = (exports.selectSnapshotForDiffing = function (
+ snapshot
+) {
+ assert(
+ snapshotIsDiffable(snapshot),
+ "To select a snapshot for diffing, it must be diffable"
+ );
+ return { type: actions.SELECT_SNAPSHOT_FOR_DIFFING, snapshot };
+});
+
+/**
+ * Compute the difference between the first and second snapshots.
+ *
+ * @param {HeapAnalysesClient} heapWorker
+ * @param {snapshotModel} first
+ * @param {snapshotModel} second
+ */
+const takeCensusDiff = (exports.takeCensusDiff = function (
+ heapWorker,
+ first,
+ second
+) {
+ return async function ({ dispatch, getState }) {
+ assert(
+ snapshotIsDiffable(first),
+ `First snapshot must be in a diffable state, found ${first.state}`
+ );
+ assert(
+ snapshotIsDiffable(second),
+ `Second snapshot must be in a diffable state, found ${second.state}`
+ );
+
+ let report, parentMap;
+ let display = getState().censusDisplay;
+ let filter = getState().filter;
+
+ if (censusIsUpToDate(filter, display, getState().diffing.census)) {
+ return;
+ }
+
+ do {
+ if (
+ !getState().diffing ||
+ getState().diffing.firstSnapshotId !== first.id ||
+ getState().diffing.secondSnapshotId !== second.id
+ ) {
+ // If we stopped diffing or stopped and then started diffing a different
+ // pair of snapshots, then just give up with diffing this pair. In the
+ // latter case, a newly spawned task will handle the diffing for the new
+ // pair.
+ return;
+ }
+
+ display = getState().censusDisplay;
+ filter = getState().filter;
+
+ dispatch({
+ type: actions.TAKE_CENSUS_DIFF_START,
+ first,
+ second,
+ filter,
+ display,
+ });
+
+ const opts = display.inverted
+ ? { asInvertedTreeNode: true }
+ : { asTreeNode: true };
+ opts.filter = filter || null;
+
+ try {
+ ({ delta: report, parentMap } = await heapWorker.takeCensusDiff(
+ first.path,
+ second.path,
+ { breakdown: display.breakdown },
+ opts
+ ));
+ } catch (error) {
+ reportException("actions/diffing/takeCensusDiff", error);
+ dispatch({ type: actions.DIFFING_ERROR, error });
+ return;
+ }
+ } while (
+ filter !== getState().filter ||
+ display !== getState().censusDisplay
+ );
+
+ dispatch({
+ type: actions.TAKE_CENSUS_DIFF_END,
+ first,
+ second,
+ report,
+ parentMap,
+ filter,
+ display,
+ });
+ };
+});
+
+/**
+ * Ensure that the current diffing data is up to date with the currently
+ * selected display, filter, etc. If the state is not up-to-date, then a
+ * recompute is triggered.
+ *
+ * @param {HeapAnalysesClient} heapWorker
+ */
+const refreshDiffing = (exports.refreshDiffing = function (heapWorker) {
+ return function ({ dispatch, getState }) {
+ if (getState().diffing.secondSnapshotId === null) {
+ return;
+ }
+
+ assert(getState().diffing.firstSnapshotId, "Should have first snapshot id");
+
+ if (getState().diffing.state === diffingState.TAKING_DIFF) {
+ // There is an existing task that will ensure that the diffing data is
+ // up-to-date.
+ return;
+ }
+
+ const { firstSnapshotId, secondSnapshotId } = getState().diffing;
+
+ const first = getSnapshot(getState(), firstSnapshotId);
+ const second = getSnapshot(getState(), secondSnapshotId);
+ dispatch(takeCensusDiff(heapWorker, first, second));
+ };
+});
+
+/**
+ * Select the given snapshot for diffing and refresh the diffing data if
+ * necessary (for example, if two snapshots are now selected for diffing).
+ *
+ * @param {HeapAnalysesClient} heapWorker
+ * @param {snapshotModel} snapshot
+ */
+exports.selectSnapshotForDiffingAndRefresh = function (heapWorker, snapshot) {
+ return async function ({ dispatch, getState }) {
+ assert(
+ getState().diffing,
+ "If we are selecting for diffing, we must be in diffing mode"
+ );
+ dispatch(selectSnapshotForDiffing(snapshot));
+ await dispatch(refreshDiffing(heapWorker));
+ };
+};
+
+/**
+ * Expand the given node in the diffing's census's delta-report.
+ *
+ * @param {CensusTreeNode} node
+ */
+exports.expandDiffingCensusNode = function (node) {
+ return {
+ type: actions.EXPAND_DIFFING_CENSUS_NODE,
+ node,
+ };
+};
+
+/**
+ * Collapse the given node in the diffing's census's delta-report.
+ *
+ * @param {CensusTreeNode} node
+ */
+exports.collapseDiffingCensusNode = function (node) {
+ return {
+ type: actions.COLLAPSE_DIFFING_CENSUS_NODE,
+ node,
+ };
+};
+
+/**
+ * Focus the given node in the snapshot's census's report.
+ *
+ * @param {DominatorTreeNode} node
+ */
+exports.focusDiffingCensusNode = function (node) {
+ return {
+ type: actions.FOCUS_DIFFING_CENSUS_NODE,
+ node,
+ };
+};