diff options
Diffstat (limited to 'devtools/client/memory/actions/diffing.js')
-rw-r--r-- | devtools/client/memory/actions/diffing.js | 221 |
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, + }; +}; |