2015-09-26 06:09:58 +03:00
|
|
|
/* 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";
|
|
|
|
|
2015-10-27 23:24:55 +03:00
|
|
|
const { assert, reportException } = require("devtools/shared/DevToolsUtils");
|
2016-01-13 23:27:30 +03:00
|
|
|
const {
|
|
|
|
censusIsUpToDate,
|
|
|
|
getSnapshot,
|
|
|
|
breakdownEquals,
|
|
|
|
createSnapshot,
|
|
|
|
dominatorTreeIsComputed,
|
|
|
|
} = require("../utils");
|
|
|
|
const { actions, snapshotState: states, viewState, dominatorTreeState } = require("../constants");
|
2016-02-03 17:17:00 +03:00
|
|
|
const telemetry = require("../telemetry");
|
2016-01-13 23:27:30 +03:00
|
|
|
const view = require("./view");
|
|
|
|
const refresh = require("./refresh");
|
2015-10-15 09:13:17 +03:00
|
|
|
|
|
|
|
/**
|
2015-11-13 22:20:45 +03:00
|
|
|
* A series of actions are fired from this task to save, read and generate the
|
|
|
|
* initial census from a snapshot.
|
2015-10-15 09:13:17 +03:00
|
|
|
*
|
|
|
|
* @param {MemoryFront}
|
|
|
|
* @param {HeapAnalysesClient}
|
|
|
|
* @param {Object}
|
|
|
|
*/
|
2015-10-17 05:15:54 +03:00
|
|
|
const takeSnapshotAndCensus = exports.takeSnapshotAndCensus = function (front, heapWorker) {
|
|
|
|
return function *(dispatch, getState) {
|
2015-11-13 22:20:45 +03:00
|
|
|
const id = yield dispatch(takeSnapshot(front));
|
|
|
|
if (id === null) {
|
|
|
|
return;
|
|
|
|
}
|
2015-10-27 23:24:55 +03:00
|
|
|
|
2015-11-13 22:20:45 +03:00
|
|
|
yield dispatch(readSnapshot(heapWorker, id));
|
|
|
|
if (getSnapshot(getState(), id).state === states.READ) {
|
|
|
|
yield dispatch(takeCensus(heapWorker, id));
|
2016-01-13 23:27:30 +03:00
|
|
|
|
|
|
|
if (getState().view === viewState.DOMINATOR_TREE) {
|
|
|
|
yield dispatch(computeAndFetchDominatorTree(heapWorker, id));
|
|
|
|
}
|
2015-10-27 23:24:55 +03:00
|
|
|
}
|
2015-10-15 09:13:17 +03:00
|
|
|
};
|
|
|
|
};
|
2015-09-26 06:09:58 +03:00
|
|
|
|
2015-10-17 05:15:54 +03:00
|
|
|
/**
|
|
|
|
* Selects a snapshot and if the snapshot's census is using a different
|
|
|
|
* breakdown, take a new census.
|
|
|
|
*
|
2015-11-13 22:20:45 +03:00
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
* @param {snapshotId} id
|
2015-10-17 05:15:54 +03:00
|
|
|
*/
|
2015-11-13 22:20:45 +03:00
|
|
|
const selectSnapshotAndRefresh = exports.selectSnapshotAndRefresh = function (heapWorker, id) {
|
2015-10-17 05:15:54 +03:00
|
|
|
return function *(dispatch, getState) {
|
2015-11-13 22:20:45 +03:00
|
|
|
if (getState().diffing) {
|
2016-01-13 23:27:30 +03:00
|
|
|
dispatch(view.changeView(viewState.CENSUS));
|
2015-11-13 22:20:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
dispatch(selectSnapshot(id));
|
2016-01-13 23:27:30 +03:00
|
|
|
yield dispatch(refresh.refresh(heapWorker));
|
2015-10-17 05:15:54 +03:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2015-10-10 18:09:28 +03:00
|
|
|
/**
|
2015-11-13 22:20:45 +03:00
|
|
|
* Take a snapshot and return its id on success, or null on failure.
|
|
|
|
*
|
|
|
|
* @param {MemoryFront} front
|
|
|
|
* @returns {Number|null}
|
2015-10-10 18:09:28 +03:00
|
|
|
*/
|
2015-10-17 05:15:54 +03:00
|
|
|
const takeSnapshot = exports.takeSnapshot = function (front) {
|
|
|
|
return function *(dispatch, getState) {
|
2016-02-03 17:17:00 +03:00
|
|
|
telemetry.countTakeSnapshot();
|
|
|
|
|
2015-11-13 22:20:45 +03:00
|
|
|
if (getState().diffing) {
|
2016-01-13 23:27:30 +03:00
|
|
|
dispatch(view.changeView(viewState.CENSUS));
|
2015-11-13 22:20:45 +03:00
|
|
|
}
|
|
|
|
|
2016-01-13 23:27:30 +03:00
|
|
|
const snapshot = createSnapshot(getState());
|
2015-11-13 22:20:45 +03:00
|
|
|
const id = snapshot.id;
|
2015-10-15 09:13:17 +03:00
|
|
|
dispatch({ type: actions.TAKE_SNAPSHOT_START, snapshot });
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch(selectSnapshot(id));
|
2015-10-15 09:13:17 +03:00
|
|
|
|
2015-10-27 23:24:55 +03:00
|
|
|
let path;
|
|
|
|
try {
|
|
|
|
path = yield front.saveHeapSnapshot();
|
|
|
|
} catch (error) {
|
|
|
|
reportException("takeSnapshot", error);
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
|
|
|
|
return null;
|
2015-10-27 23:24:55 +03:00
|
|
|
}
|
2015-10-15 09:13:17 +03:00
|
|
|
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch({ type: actions.TAKE_SNAPSHOT_END, id, path });
|
|
|
|
return snapshot.id;
|
2015-10-15 09:13:17 +03:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads a snapshot into memory; necessary to do before taking
|
|
|
|
* a census on the snapshot. May only be called once per snapshot.
|
|
|
|
*
|
2015-11-13 22:20:45 +03:00
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
* @param {snapshotId} id
|
2015-10-15 09:13:17 +03:00
|
|
|
*/
|
2015-11-13 22:20:45 +03:00
|
|
|
const readSnapshot = exports.readSnapshot = function readSnapshot (heapWorker, id) {
|
2015-10-17 05:15:54 +03:00
|
|
|
return function *(dispatch, getState) {
|
2015-11-13 22:20:45 +03:00
|
|
|
const snapshot = getSnapshot(getState(), id);
|
2015-11-11 20:52:36 +03:00
|
|
|
assert([states.SAVED, states.IMPORTING].includes(snapshot.state),
|
2015-10-27 23:24:55 +03:00
|
|
|
`Should only read a snapshot once. Found snapshot in state ${snapshot.state}`);
|
2015-10-15 09:13:17 +03:00
|
|
|
|
2015-10-29 07:27:46 +03:00
|
|
|
let creationTime;
|
|
|
|
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch({ type: actions.READ_SNAPSHOT_START, id });
|
2015-10-27 23:24:55 +03:00
|
|
|
try {
|
|
|
|
yield heapWorker.readHeapSnapshot(snapshot.path);
|
2015-10-29 07:27:46 +03:00
|
|
|
creationTime = yield heapWorker.getCreationTime(snapshot.path);
|
2015-10-27 23:24:55 +03:00
|
|
|
} catch (error) {
|
|
|
|
reportException("readSnapshot", error);
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
|
2015-10-27 23:24:55 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch({ type: actions.READ_SNAPSHOT_END, id, creationTime });
|
2015-10-15 09:13:17 +03:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
2015-11-13 22:20:45 +03:00
|
|
|
* @param {snapshotId} id
|
2015-10-15 09:13:17 +03:00
|
|
|
*
|
2015-10-17 05:15:54 +03:00
|
|
|
* @see {Snapshot} model defined in devtools/client/memory/models.js
|
2015-10-15 09:13:17 +03:00
|
|
|
* @see `devtools/shared/heapsnapshot/HeapAnalysesClient.js`
|
|
|
|
* @see `js/src/doc/Debugger/Debugger.Memory.md` for breakdown details
|
|
|
|
*/
|
2015-11-13 22:20:45 +03:00
|
|
|
const takeCensus = exports.takeCensus = function (heapWorker, id) {
|
2015-10-17 05:15:54 +03:00
|
|
|
return function *(dispatch, getState) {
|
2015-11-13 22:20:45 +03:00
|
|
|
const snapshot = getSnapshot(getState(), id);
|
2015-10-15 09:13:17 +03:00
|
|
|
assert([states.READ, states.SAVED_CENSUS].includes(snapshot.state),
|
2015-10-27 23:24:55 +03:00
|
|
|
`Can only take census of snapshots in READ or SAVED_CENSUS state, found ${snapshot.state}`);
|
2015-10-15 09:13:17 +03:00
|
|
|
|
2016-02-06 02:11:48 +03:00
|
|
|
let report, parentMap;
|
2015-10-27 19:11:05 +03:00
|
|
|
let inverted = getState().inverted;
|
2015-10-17 05:15:54 +03:00
|
|
|
let breakdown = getState().breakdown;
|
2015-11-05 03:12:31 +03:00
|
|
|
let filter = getState().filter;
|
2015-10-17 05:15:54 +03:00
|
|
|
|
2015-11-05 03:12:31 +03:00
|
|
|
// If breakdown, filter and inversion haven't changed, don't do anything.
|
2015-11-13 22:20:45 +03:00
|
|
|
if (censusIsUpToDate(inverted, filter, breakdown, snapshot.census)) {
|
2015-10-17 05:15:54 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep taking a census if the breakdown changes during. Recheck
|
|
|
|
// that the breakdown used for the census is the same as
|
|
|
|
// the state's breakdown.
|
|
|
|
do {
|
2015-10-27 19:11:05 +03:00
|
|
|
inverted = getState().inverted;
|
2015-10-17 05:15:54 +03:00
|
|
|
breakdown = getState().breakdown;
|
2015-11-05 03:12:31 +03:00
|
|
|
filter = getState().filter;
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: actions.TAKE_CENSUS_START,
|
2015-11-13 22:20:45 +03:00
|
|
|
id,
|
2015-11-05 03:12:31 +03:00
|
|
|
inverted,
|
|
|
|
filter,
|
|
|
|
breakdown
|
|
|
|
});
|
|
|
|
|
2015-10-27 19:11:05 +03:00
|
|
|
let opts = inverted ? { asInvertedTreeNode: true } : { asTreeNode: true };
|
2015-11-05 03:12:31 +03:00
|
|
|
opts.filter = filter || null;
|
2015-10-27 23:24:55 +03:00
|
|
|
|
|
|
|
try {
|
2016-02-06 02:11:48 +03:00
|
|
|
({ report, parentMap } = yield heapWorker.takeCensus(snapshot.path,
|
|
|
|
{ breakdown },
|
|
|
|
opts));
|
2015-11-13 22:20:45 +03:00
|
|
|
} catch (error) {
|
2015-10-27 23:24:55 +03:00
|
|
|
reportException("takeCensus", error);
|
2015-11-13 22:20:45 +03:00
|
|
|
dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
|
2015-10-27 23:24:55 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (inverted !== getState().inverted ||
|
2015-11-05 03:12:31 +03:00
|
|
|
filter !== getState().filter ||
|
2015-10-27 23:24:55 +03:00
|
|
|
!breakdownEquals(breakdown, getState().breakdown));
|
2015-10-15 09:13:17 +03:00
|
|
|
|
2015-11-05 03:12:31 +03:00
|
|
|
dispatch({
|
|
|
|
type: actions.TAKE_CENSUS_END,
|
2015-11-13 22:20:45 +03:00
|
|
|
id,
|
2015-11-05 03:12:31 +03:00
|
|
|
breakdown,
|
|
|
|
inverted,
|
|
|
|
filter,
|
2016-02-06 02:11:48 +03:00
|
|
|
report,
|
|
|
|
parentMap
|
2015-11-05 03:12:31 +03:00
|
|
|
});
|
2016-02-03 17:17:00 +03:00
|
|
|
|
|
|
|
telemetry.countCensus({ inverted, filter, breakdown });
|
2015-10-27 19:11:05 +03:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh the selected snapshot's census data, if need be (for example,
|
|
|
|
* breakdown configuration changed).
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
*/
|
|
|
|
const refreshSelectedCensus = exports.refreshSelectedCensus = function (heapWorker) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
let snapshot = getState().snapshots.find(s => s.selected);
|
|
|
|
|
|
|
|
// Intermediate snapshot states will get handled by the task action that is
|
|
|
|
// orchestrating them. For example, if the snapshot's state is
|
|
|
|
// SAVING_CENSUS, then the takeCensus action will keep taking a census until
|
|
|
|
// the inverted property matches the inverted state. If the snapshot is
|
|
|
|
// still in the process of being saved or read, the takeSnapshotAndCensus
|
|
|
|
// task action will follow through and ensure that a census is taken.
|
|
|
|
if (snapshot && snapshot.state === states.SAVED_CENSUS) {
|
2015-11-13 22:20:45 +03:00
|
|
|
yield dispatch(takeCensus(heapWorker, snapshot.id));
|
2015-10-27 19:11:05 +03:00
|
|
|
}
|
2015-09-26 06:09:58 +03:00
|
|
|
};
|
|
|
|
};
|
2015-10-10 18:09:28 +03:00
|
|
|
|
2016-01-13 23:27:30 +03:00
|
|
|
/**
|
|
|
|
* Request that the `HeapAnalysesWorker` compute the dominator tree for the
|
|
|
|
* snapshot with the given `id`.
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
* @param {SnapshotId} id
|
|
|
|
*
|
|
|
|
* @returns {Promise<DominatorTreeId>}
|
|
|
|
*/
|
|
|
|
const computeDominatorTree = exports.computeDominatorTree = function (heapWorker, id) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
const snapshot = getSnapshot(getState(), id);
|
|
|
|
assert(!(snapshot.dominatorTree && snapshot.dominatorTree.dominatorTreeId),
|
|
|
|
"Should not re-compute dominator trees");
|
|
|
|
|
|
|
|
dispatch({ type: actions.COMPUTE_DOMINATOR_TREE_START, id });
|
|
|
|
|
|
|
|
let dominatorTreeId;
|
|
|
|
try {
|
|
|
|
dominatorTreeId = yield heapWorker.computeDominatorTree(snapshot.path);
|
|
|
|
} catch (error) {
|
|
|
|
reportException("actions/snapshot/computeDominatorTree", error);
|
|
|
|
dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
dispatch({ type: actions.COMPUTE_DOMINATOR_TREE_END, id, dominatorTreeId });
|
|
|
|
return dominatorTreeId;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the partial subtree, starting from the root, of the
|
|
|
|
* snapshot-with-the-given-id's dominator tree.
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
* @param {SnapshotId} id
|
|
|
|
*
|
|
|
|
* @returns {Promise<DominatorTreeNode>}
|
|
|
|
*/
|
|
|
|
const fetchDominatorTree = exports.fetchDominatorTree = function (heapWorker, id) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
const snapshot = getSnapshot(getState(), id);
|
|
|
|
assert(dominatorTreeIsComputed(snapshot),
|
|
|
|
"Should have dominator tree model and it should be computed");
|
|
|
|
|
|
|
|
let breakdown;
|
|
|
|
let root;
|
|
|
|
do {
|
|
|
|
breakdown = getState().dominatorTreeBreakdown;
|
|
|
|
assert(breakdown, "Should have a breakdown to describe nodes with.");
|
|
|
|
|
|
|
|
dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, breakdown });
|
|
|
|
|
|
|
|
try {
|
|
|
|
root = yield heapWorker.getDominatorTree({
|
|
|
|
dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
|
|
|
|
breakdown,
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
reportException("actions/snapshot/fetchDominatorTree", error);
|
|
|
|
dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!breakdownEquals(breakdown, getState().dominatorTreeBreakdown));
|
|
|
|
|
|
|
|
dispatch({ type: actions.FETCH_DOMINATOR_TREE_END, id, root });
|
2016-02-03 17:17:00 +03:00
|
|
|
telemetry.countDominatorTree({ breakdown });
|
2016-01-13 23:27:30 +03:00
|
|
|
return root;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch the immediately dominated children represented by the placeholder
|
|
|
|
* `lazyChildren` from snapshot-with-the-given-id's dominator tree.
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
* @param {SnapshotId} id
|
|
|
|
* @param {DominatorTreeLazyChildren} lazyChildren
|
|
|
|
*/
|
|
|
|
const fetchImmediatelyDominated = exports.fetchImmediatelyDominated = function (heapWorker, id, lazyChildren) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
const snapshot = getSnapshot(getState(), id);
|
|
|
|
assert(snapshot.dominatorTree, "Should have dominator tree model");
|
|
|
|
assert(snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
|
|
|
|
snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
|
|
|
|
"Cannot fetch immediately dominated nodes in a dominator tree unless " +
|
|
|
|
" the dominator tree has already been computed");
|
|
|
|
|
|
|
|
let breakdown;
|
|
|
|
let response;
|
|
|
|
do {
|
|
|
|
breakdown = getState().dominatorTreeBreakdown;
|
|
|
|
assert(breakdown, "Should have a breakdown to describe nodes with.");
|
|
|
|
|
|
|
|
dispatch({ type: actions.FETCH_IMMEDIATELY_DOMINATED_START, id });
|
|
|
|
|
|
|
|
try {
|
|
|
|
response = yield heapWorker.getImmediatelyDominated({
|
|
|
|
dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
|
|
|
|
breakdown,
|
|
|
|
nodeId: lazyChildren.parentNodeId(),
|
|
|
|
startIndex: lazyChildren.siblingIndex(),
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
reportException("actions/snapshot/fetchImmediatelyDominated", error);
|
|
|
|
dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!breakdownEquals(breakdown, getState().dominatorTreeBreakdown));
|
|
|
|
|
|
|
|
dispatch({
|
|
|
|
type: actions.FETCH_IMMEDIATELY_DOMINATED_END,
|
|
|
|
id,
|
|
|
|
path: response.path,
|
|
|
|
nodes: response.nodes,
|
|
|
|
moreChildrenAvailable: response.moreChildrenAvailable,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compute and then fetch the dominator tree of the snapshot with the given
|
|
|
|
* `id`.
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
* @param {SnapshotId} id
|
|
|
|
*
|
|
|
|
* @returns {Promise<DominatorTreeNode>}
|
|
|
|
*/
|
|
|
|
const computeAndFetchDominatorTree = exports.computeAndFetchDominatorTree = function (heapWorker, id) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
const dominatorTreeId = yield dispatch(computeDominatorTree(heapWorker, id));
|
|
|
|
if (dominatorTreeId === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = yield dispatch(fetchDominatorTree(heapWorker, id));
|
|
|
|
if (!root) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return root;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the currently selected snapshot's dominator tree.
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
*/
|
|
|
|
const refreshSelectedDominatorTree = exports.refreshSelectedDominatorTree = function (heapWorker) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
let snapshot = getState().snapshots.find(s => s.selected);
|
|
|
|
if (!snapshot) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (snapshot.dominatorTree &&
|
|
|
|
!(snapshot.dominatorTree.state === dominatorTreeState.COMPUTED ||
|
|
|
|
snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
|
|
|
|
snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (snapshot.state) {
|
|
|
|
case states.READ:
|
|
|
|
case states.SAVING_CENSUS:
|
|
|
|
case states.SAVED_CENSUS:
|
|
|
|
if (snapshot.dominatorTree) {
|
|
|
|
yield dispatch(fetchDominatorTree(heapWorker, snapshot.id));
|
|
|
|
} else {
|
|
|
|
yield dispatch(computeAndFetchDominatorTree(heapWorker, snapshot.id));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// If there was an error, we can't continue. If we are still saving or
|
|
|
|
// reading the snapshot, then takeSnapshotAndCensus will finish the job
|
|
|
|
// for us.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2015-10-10 18:09:28 +03:00
|
|
|
/**
|
2015-11-13 22:20:45 +03:00
|
|
|
* Select the snapshot with the given id.
|
|
|
|
*
|
|
|
|
* @param {snapshotId} id
|
2015-10-17 05:15:54 +03:00
|
|
|
* @see {Snapshot} model defined in devtools/client/memory/models.js
|
2015-10-10 18:09:28 +03:00
|
|
|
*/
|
2015-11-13 22:20:45 +03:00
|
|
|
const selectSnapshot = exports.selectSnapshot = function (id) {
|
2015-10-10 18:09:28 +03:00
|
|
|
return {
|
|
|
|
type: actions.SELECT_SNAPSHOT,
|
2015-11-13 22:20:45 +03:00
|
|
|
id
|
2015-10-10 18:09:28 +03:00
|
|
|
};
|
|
|
|
};
|
2016-01-13 23:27:30 +03:00
|
|
|
|
2016-01-12 21:14:15 +03:00
|
|
|
/**
|
|
|
|
* Delete all snapshots that are in the SAVED_CENSUS or ERROR state
|
|
|
|
*
|
|
|
|
* @param {HeapAnalysesClient} heapWorker
|
|
|
|
*/
|
|
|
|
const clearSnapshots = exports.clearSnapshots = function (heapWorker) {
|
|
|
|
return function*(dispatch, getState) {
|
|
|
|
let snapshots = getState().snapshots.filter(
|
|
|
|
s => s.state === states.SAVED_CENSUS || s.state === states.ERROR);
|
|
|
|
|
|
|
|
let ids = snapshots.map(s => s.id);
|
|
|
|
|
|
|
|
dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids });
|
|
|
|
|
2016-02-02 17:09:00 +03:00
|
|
|
yield Promise.all(snapshots.map(snapshot => {
|
|
|
|
return heapWorker.deleteHeapSnapshot(snapshot.path).catch(error => {
|
2016-01-12 21:14:15 +03:00
|
|
|
reportException("clearSnapshots", error);
|
2016-02-02 17:09:00 +03:00
|
|
|
dispatch({ type: actions.SNAPSHOT_ERROR, id: snapshot.id, error });
|
2016-01-12 21:14:15 +03:00
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids });
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2016-01-13 23:27:30 +03:00
|
|
|
/**
|
|
|
|
* Expand the given node in the snapshot's census report.
|
|
|
|
*
|
|
|
|
* @param {CensusTreeNode} node
|
|
|
|
*/
|
|
|
|
const expandCensusNode = exports.expandCensusNode = function (id, node) {
|
|
|
|
return {
|
|
|
|
type: actions.EXPAND_CENSUS_NODE,
|
|
|
|
id,
|
|
|
|
node,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collapse the given node in the snapshot's census report.
|
|
|
|
*
|
|
|
|
* @param {CensusTreeNode} node
|
|
|
|
*/
|
|
|
|
const collapseCensusNode = exports.collapseCensusNode = function (id, node) {
|
|
|
|
return {
|
|
|
|
type: actions.COLLAPSE_CENSUS_NODE,
|
|
|
|
id,
|
|
|
|
node,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Focus the given node in the snapshot's census's report.
|
|
|
|
*
|
|
|
|
* @param {SnapshotId} id
|
|
|
|
* @param {DominatorTreeNode} node
|
|
|
|
*/
|
|
|
|
const focusCensusNode = exports.focusCensusNode = function (id, node) {
|
|
|
|
return {
|
|
|
|
type: actions.FOCUS_CENSUS_NODE,
|
|
|
|
id,
|
|
|
|
node,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expand the given node in the snapshot's dominator tree.
|
|
|
|
*
|
|
|
|
* @param {DominatorTreeTreeNode} node
|
|
|
|
*/
|
|
|
|
const expandDominatorTreeNode = exports.expandDominatorTreeNode = function (id, node) {
|
|
|
|
return {
|
|
|
|
type: actions.EXPAND_DOMINATOR_TREE_NODE,
|
|
|
|
id,
|
|
|
|
node,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collapse the given node in the snapshot's dominator tree.
|
|
|
|
*
|
|
|
|
* @param {DominatorTreeTreeNode} node
|
|
|
|
*/
|
|
|
|
const collapseDominatorTreeNode = exports.collapseDominatorTreeNode = function (id, node) {
|
|
|
|
return {
|
|
|
|
type: actions.COLLAPSE_DOMINATOR_TREE_NODE,
|
|
|
|
id,
|
|
|
|
node,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Focus the given node in the snapshot's dominator tree.
|
|
|
|
*
|
|
|
|
* @param {SnapshotId} id
|
|
|
|
* @param {DominatorTreeNode} node
|
|
|
|
*/
|
|
|
|
const focusDominatorTreeNode = exports.focusDominatorTreeNode = function (id, node) {
|
|
|
|
return {
|
|
|
|
type: actions.FOCUS_DOMINATOR_TREE_NODE,
|
|
|
|
id,
|
|
|
|
node,
|
|
|
|
};
|
|
|
|
};
|