221 lines
5.7 KiB
JavaScript
221 lines
5.7 KiB
JavaScript
/* 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,
|
|
};
|
|
};
|