Bug 1215955 - Add clear snapshots button to devtools memory tool. r=fitzgen

This commit is contained in:
Julian Descottes 2016-01-12 19:14:15 +01:00
parent 707f31a5b2
commit 484c41bf4d
16 changed files with 327 additions and 4 deletions

View File

@ -84,6 +84,10 @@ take-snapshot=Take snapshot
# initiates importing a snapshot.
import-snapshot=Import…
# LOCALIZATION NOTE (clear-snapshots): The label describing the button that clears
# existing snapshot.
clear-snapshots=Clear
# LOCALIZATION NOTE (diff-snapshots): The label for the button that initiates
# selecting two snapshots to diff with each other.
diff-snapshots=+/-

View File

@ -406,6 +406,32 @@ const selectSnapshot = exports.selectSnapshot = function (id) {
};
};
/**
* 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 });
Promise.all(snapshots.map(s => {
heapWorker.deleteHeapSnapshot(s.path)
.catch(error => {
reportException("clearSnapshots", error);
dispatch({ type: actions.SNAPSHOT_ERROR, id: s.id, error });
});
}));
dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids });
};
};
/**
* Expand the given node in the snapshot's census report.
*

View File

@ -22,6 +22,7 @@ const { pickFileAndExportSnapshot, pickFileAndImportSnapshotAndCensus } = requir
const {
selectSnapshotAndRefresh,
takeSnapshotAndCensus,
clearSnapshots,
fetchImmediatelyDominated,
expandCensusNode,
collapseCensusNode,
@ -98,6 +99,7 @@ const MemoryApp = createClass({
snapshots,
breakdowns: getBreakdownDisplayData(),
onImportClick: () => dispatch(pickFileAndImportSnapshotAndCensus(heapWorker)),
onClearSnapshotsClick: () => dispatch(clearSnapshots(heapWorker)),
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
onBreakdownChange: breakdown =>
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),

View File

@ -17,6 +17,7 @@ const Toolbar = module.exports = createClass({
})).isRequired,
onTakeSnapshotClick: PropTypes.func.isRequired,
onImportClick: PropTypes.func.isRequired,
onClearSnapshotsClick: PropTypes.func.isRequired,
onBreakdownChange: PropTypes.func.isRequired,
onToggleRecordAllocationStacks: PropTypes.func.isRequired,
allocations: models.allocations,
@ -40,6 +41,7 @@ const Toolbar = module.exports = createClass({
let {
onTakeSnapshotClick,
onImportClick,
onClearSnapshotsClick,
onBreakdownChange,
breakdowns,
dominatorTreeBreakdowns,
@ -190,7 +192,15 @@ const Toolbar = module.exports = createClass({
"data-text-only": true,
},
L10N.getStr("import-snapshot")
)
),
dom.button({
id: "clear-snapshots",
className: "devtools-toolbarbutton clear-snapshots devtools-button",
onClick: onClearSnapshotsClick,
title: L10N.getStr("clear-snapshots"),
"data-text-only": true,
}, L10N.getStr("clear-snapshots"))
),
dom.label(

View File

@ -50,6 +50,10 @@ actions.IMPORT_SNAPSHOT_ERROR = "import-snapshot-error";
// Fired by UI to select a snapshot to view.
actions.SELECT_SNAPSHOT = "select-snapshot";
// Fired to delete a provided list of snapshots
actions.DELETE_SNAPSHOTS_START = "delete-snapshots-start";
actions.DELETE_SNAPSHOTS_END = "delete-snapshots-end";
// Fired to toggle tree inversion on or off.
actions.TOGGLE_INVERTED = "toggle-inverted";

View File

@ -138,6 +138,14 @@ handlers[actions.SELECT_SNAPSHOT] = function (snapshots, { id }) {
return snapshots.map(s => immutableUpdate(s, { selected: s.id === id }));
};
handlers[actions.DELETE_SNAPSHOTS_START] = function (snapshots, { ids }) {
return snapshots.filter(s => ids.indexOf(s.id) === -1);
};
handlers[actions.DELETE_SNAPSHOTS_END] = function (snapshots) {
return snapshots;
};
handlers[actions.CHANGE_VIEW] = function (snapshots, { view }) {
return view === viewState.DIFFING
? snapshots.map(s => immutableUpdate(s, { selected: false }))

View File

@ -18,6 +18,8 @@ support-files =
[browser_memory_no_auto_expand.js]
skip-if = debug # bug 1219554
[browser_memory_percents_01.js]
[browser_memory-clear-snapshots.js]
skip-if = debug # bug 1219554
[browser_memory-simple-01.js]
skip-if = debug # bug 1219554
[browser_memory_transferHeapSnapshot_e10s_01.js]

View File

@ -0,0 +1,31 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests taking and then clearing snapshots.
*/
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { gStore, document } = panel.panelWin;
const { getState, dispatch } = gStore;
let snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
is(getState().snapshots.length, 0, "Starts with no snapshots in store");
is(snapshotEls.length, 0, "No snapshots visible");
info("Take two snapshots");
yield takeSnapshot(panel.panelWin);
yield takeSnapshot(panel.panelWin);
yield waitUntilSnapshotState(gStore, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
is(snapshotEls.length, 2, "Two snapshots visible");
info("Click on Clear Snapshots");
yield clearSnapshots(panel.panelWin);
is(getState().snapshots.length, 0, "No snapshots in store");
snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
is(snapshotEls.length, 0, "No snapshot visible");
});

View File

@ -104,6 +104,14 @@ function takeSnapshot (window) {
return waitUntilState(gStore, () => gStore.getState().snapshots.length === snapshotCount + 1);
}
function clearSnapshots (window) {
let { gStore, document } = window;
document.querySelector(".devtools-toolbar .clear-snapshots").click();
return waitUntilState(gStore, () => gStore.getState().snapshots.every(
(snapshot) => snapshot.state !== states.SAVED_CENSUS)
);
}
/**
* Sets breakdown and waits for currently selected breakdown to use it
* and be completed the census.

View File

@ -0,0 +1,37 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test clearSnapshots deletes snapshots with state SAVED_CENSUS
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
let { snapshotState: states, actions } = require("devtools/client/memory/constants");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(true, "snapshot created");
ok(true, "dispatch clearSnapshots action");
let deleteEvents = Promise.all([
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
]);
dispatch(clearSnapshots(heapWorker));
yield deleteEvents;
ok(true, "received delete snapshots events");
equal(getState().snapshots.length, 0, "no snapshot remaining");
heapWorker.destroy();
yield front.detach();
});

View File

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test clearSnapshots preserves snapshots with state != SAVED_CENSUS or ERROR
let { takeSnapshotAndCensus, clearSnapshots, takeSnapshot } = require("devtools/client/memory/actions/snapshot");
let { snapshotState: states, actions } = require("devtools/client/memory/constants");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
ok(true, "create a snapshot in SAVED_CENSUS state");
dispatch(takeSnapshotAndCensus(front, heapWorker));
ok(true, "create a snapshot in SAVED state");
dispatch(takeSnapshot(front));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED]);
ok(true, "snapshots created with expected states");
ok(true, "dispatch clearSnapshots action");
let deleteEvents = Promise.all([
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
]);
dispatch(clearSnapshots(heapWorker));
yield deleteEvents;
ok(true, "received delete snapshots events");
equal(getState().snapshots.length, 1, "one snapshot remaining");
let remainingSnapshot = getState().snapshots[0];
notEqual(remainingSnapshot.state, states.SAVED_CENSUS,
"remaining snapshot doesn't have the SAVED_CENSUS state");
heapWorker.destroy();
yield front.detach();
});

View File

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test clearSnapshots deletes snapshots with state ERROR
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
let { snapshotState: states, actions } = require("devtools/client/memory/constants");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
ok(true, "create a snapshot with SAVED_CENSUS state");
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(true, "snapshot created with SAVED_CENSUS state");
ok(true, "set snapshot state to error");
let id = getState().snapshots[0].id;
dispatch({ type: actions.SNAPSHOT_ERROR, id, error: new Error("_") });
yield waitUntilSnapshotState(store, [states.ERROR]);
ok(true, "snapshot set to error state");
ok(true, "dispatch clearSnapshots action");
let deleteEvents = Promise.all([
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
]);
dispatch(clearSnapshots(heapWorker));
yield deleteEvents;
ok(true, "received delete snapshots events");
equal(getState().snapshots.length, 0, "error snapshot deleted");
heapWorker.destroy();
yield front.detach();
});

View File

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test clearSnapshots deletes several snapshots
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
let { snapshotState: states, actions } = require("devtools/client/memory/constants");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
ok(true, "create 3 snapshots in SAVED_CENSUS state");
dispatch(takeSnapshotAndCensus(front, heapWorker));
dispatch(takeSnapshotAndCensus(front, heapWorker));
dispatch(takeSnapshotAndCensus(front, heapWorker));
ok(true, "snapshots created in SAVED_CENSUS state");
yield waitUntilSnapshotState(store,
[states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(true, "set first snapshot state to error");
let id = getState().snapshots[0].id;
dispatch({ type: actions.SNAPSHOT_ERROR, id, error: new Error("_") });
yield waitUntilSnapshotState(store,
[states.ERROR, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(true, "first snapshot set to error state");
ok(true, "dispatch clearSnapshots action");
let deleteEvents = Promise.all([
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
]);
dispatch(clearSnapshots(heapWorker));
yield deleteEvents;
ok(true, "received delete snapshots events");
equal(getState().snapshots.length, 0, "no snapshot remaining");
heapWorker.destroy();
yield front.detach();
});

View File

@ -0,0 +1,46 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test clearSnapshots deletes several snapshots
let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
let { snapshotState: states, actions } = require("devtools/client/memory/constants");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
ok(true, "create 3 snapshots in SAVED_CENSUS state");
dispatch(takeSnapshotAndCensus(front, heapWorker));
dispatch(takeSnapshotAndCensus(front, heapWorker));
ok(true, "snapshots created in SAVED_CENSUS state");
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
let errorHeapWorker = {
deleteHeapSnapshot: function() {
return Promise.reject("_");
}
};
ok(true, "dispatch clearSnapshots action");
let deleteEvents = Promise.all([
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END),
waitUntilAction(store, actions.SNAPSHOT_ERROR),
waitUntilAction(store, actions.SNAPSHOT_ERROR),
]);
dispatch(clearSnapshots(errorHeapWorker));
yield deleteEvents;
ok(true, "received delete snapshots and snapshot error events");
equal(getState().snapshots.length, 0, "no snapshot remaining");
heapWorker.destroy();
yield front.detach();
});

View File

@ -10,6 +10,11 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action_diffing_03.js]
[test_action_diffing_04.js]
[test_action_diffing_05.js]
[test_action-clear-snapshots_01.js]
[test_action-clear-snapshots_02.js]
[test_action-clear-snapshots_03.js]
[test_action-clear-snapshots_04.js]
[test_action-clear-snapshots_05.js]
[test_action-export-snapshot.js]
[test_action-filter-01.js]
[test_action-filter-02.js]

View File

@ -58,9 +58,10 @@ html, body, #app, #memory-tool {
* We want this to be exactly at a `--sidebar-width` distance from the
* toolbar's start boundary. A `.devtools-toolbar` has a 3px start padding.
*/
flex: 0 0 calc(var(--sidebar-width) - 3px);
flex: 0 0 calc(var(--sidebar-width) - 4px);
border-inline-end: 1px solid var(--theme-splitter-color);
margin-inline-end: 5px;
padding-right: 1px;
}
.devtools-toolbar > .toolbar-group {
@ -111,8 +112,13 @@ html, body, #app, #memory-tool {
* Due to toolbar styles of `.devtools-toolbarbutton:not([label])` which overrides
* .devtools-toolbarbutton's min-width of 78px, reset the min-width.
*/
#import-snapshot {
min-width: 78px;
#import-snapshot,
#clear-snapshots {
-moz-box-align: center;
flex-grow: 1;
padding: 1px;
margin: 2px 1px;
min-width: unset;
}
.spacer {