зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1214066 - Add the ability to toggle allocation stack recording; r=jsantell
This commit is contained in:
Родитель
89efe3149f
Коммит
554bd61388
|
@ -0,0 +1,20 @@
|
||||||
|
/* 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 { actions, ALLOCATION_RECORDING_OPTIONS } = require("../constants");
|
||||||
|
|
||||||
|
exports.toggleRecordingAllocationStacks = function (front) {
|
||||||
|
return function* (dispatch, getState) {
|
||||||
|
dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_START });
|
||||||
|
|
||||||
|
if (getState().recordingAllocationStacks) {
|
||||||
|
yield front.stopRecordingAllocations();
|
||||||
|
} else {
|
||||||
|
yield front.startRecordingAllocations(ALLOCATION_RECORDING_OPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_END });
|
||||||
|
};
|
||||||
|
};
|
|
@ -4,6 +4,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
|
'allocations.js',
|
||||||
'breakdown.js',
|
'breakdown.js',
|
||||||
'snapshot.js',
|
'snapshot.js',
|
||||||
)
|
)
|
||||||
|
|
|
@ -122,4 +122,3 @@ const selectSnapshot = exports.selectSnapshot = function (snapshot) {
|
||||||
snapshot
|
snapshot
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
|
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
const { selectSnapshotAndRefresh, takeSnapshotAndCensus } = require("./actions/snapshot");
|
const { toggleRecordingAllocationStacks } = require("./actions/allocations");
|
||||||
const { setBreakdownAndRefresh } = require("./actions/breakdown");
|
const { setBreakdownAndRefresh } = require("./actions/breakdown");
|
||||||
|
const { selectSnapshotAndRefresh, takeSnapshotAndCensus } = require("./actions/snapshot");
|
||||||
const { breakdownNameToSpec, getBreakdownDisplayData } = require("./utils");
|
const { breakdownNameToSpec, getBreakdownDisplayData } = require("./utils");
|
||||||
const Toolbar = createFactory(require("./components/toolbar"));
|
const Toolbar = createFactory(require("./components/toolbar"));
|
||||||
const List = createFactory(require("./components/list"));
|
const List = createFactory(require("./components/list"));
|
||||||
|
@ -31,7 +32,15 @@ const App = createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { dispatch, snapshots, front, heapWorker, breakdown } = this.props;
|
let {
|
||||||
|
dispatch,
|
||||||
|
snapshots,
|
||||||
|
front,
|
||||||
|
heapWorker,
|
||||||
|
breakdown,
|
||||||
|
allocations,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
let selectedSnapshot = snapshots.find(s => s.selected);
|
let selectedSnapshot = snapshots.find(s => s.selected);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -42,6 +51,9 @@ const App = createClass({
|
||||||
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
|
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
|
||||||
onBreakdownChange: breakdown =>
|
onBreakdownChange: breakdown =>
|
||||||
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),
|
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),
|
||||||
|
onToggleRecordAllocationStacks: () =>
|
||||||
|
dispatch(toggleRecordingAllocationStacks(front)),
|
||||||
|
allocations
|
||||||
}),
|
}),
|
||||||
|
|
||||||
dom.div({ id: "memory-tool-container" }, [
|
dom.div({ id: "memory-tool-container" }, [
|
||||||
|
@ -66,7 +78,10 @@ const App = createClass({
|
||||||
* and passed to components.
|
* and passed to components.
|
||||||
*/
|
*/
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return { snapshots: state.snapshots };
|
return {
|
||||||
|
allocations: state.allocations,
|
||||||
|
snapshots: state.snapshots
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(App);
|
module.exports = connect(mapStateToProps)(App);
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||||
|
|
||||||
|
const models = require("../models");
|
||||||
|
|
||||||
const Toolbar = module.exports = createClass({
|
const Toolbar = module.exports = createClass({
|
||||||
displayName: "toolbar",
|
displayName: "toolbar",
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -13,17 +15,38 @@ const Toolbar = module.exports = createClass({
|
||||||
})).isRequired,
|
})).isRequired,
|
||||||
onTakeSnapshotClick: PropTypes.func.isRequired,
|
onTakeSnapshotClick: PropTypes.func.isRequired,
|
||||||
onBreakdownChange: PropTypes.func.isRequired,
|
onBreakdownChange: PropTypes.func.isRequired,
|
||||||
|
onToggleRecordAllocationStacks: PropTypes.func.isRequired,
|
||||||
|
allocations: models.allocations
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { onTakeSnapshotClick, onBreakdownChange, breakdowns } = this.props;
|
let {
|
||||||
|
onTakeSnapshotClick,
|
||||||
|
onBreakdownChange,
|
||||||
|
breakdowns,
|
||||||
|
onToggleRecordAllocationStacks,
|
||||||
|
allocations,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
DOM.div({ className: "devtools-toolbar" }, [
|
DOM.div({ className: "devtools-toolbar" }, [
|
||||||
DOM.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
|
DOM.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
|
||||||
|
|
||||||
DOM.select({
|
DOM.select({
|
||||||
className: `select-breakdown`,
|
className: `select-breakdown`,
|
||||||
onChange: e => onBreakdownChange(e.target.value),
|
onChange: e => onBreakdownChange(e.target.value),
|
||||||
}, breakdowns.map(({ name, displayName }) => DOM.option({ value: name }, displayName)))
|
}, breakdowns.map(({ name, displayName }) => DOM.option({ value: name }, displayName))),
|
||||||
|
|
||||||
|
DOM.label({}, [
|
||||||
|
DOM.input({
|
||||||
|
type: "checkbox",
|
||||||
|
checked: allocations.recording,
|
||||||
|
disabled: allocations.togglingInProgress,
|
||||||
|
onChange: onToggleRecordAllocationStacks,
|
||||||
|
}),
|
||||||
|
// TODO bug 1214799
|
||||||
|
"Record allocation stacks"
|
||||||
|
])
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,19 @@ actions.READ_SNAPSHOT_END = "read-snapshot-end";
|
||||||
actions.TAKE_CENSUS_START = "take-census-start";
|
actions.TAKE_CENSUS_START = "take-census-start";
|
||||||
actions.TAKE_CENSUS_END = "take-census-end";
|
actions.TAKE_CENSUS_END = "take-census-end";
|
||||||
|
|
||||||
|
// When requesting that the server start/stop recording allocation stacks.
|
||||||
|
actions.TOGGLE_RECORD_ALLOCATION_STACKS_START = "toggle-record-allocation-stacks-start";
|
||||||
|
actions.TOGGLE_RECORD_ALLOCATION_STACKS_END = "toggle-record-allocation-stacks-end";
|
||||||
|
|
||||||
// Fired by UI to select a snapshot to view.
|
// Fired by UI to select a snapshot to view.
|
||||||
actions.SELECT_SNAPSHOT = "select-snapshot";
|
actions.SELECT_SNAPSHOT = "select-snapshot";
|
||||||
|
|
||||||
|
// Options passed to MemoryFront's startRecordingAllocations never change.
|
||||||
|
exports.ALLOCATION_RECORDING_OPTIONS = {
|
||||||
|
probability: 1,
|
||||||
|
maxLogLength: 1
|
||||||
|
};
|
||||||
|
|
||||||
const COUNT = { by: "count", count: true, bytes: true };
|
const COUNT = { by: "count", count: true, bytes: true };
|
||||||
const INTERNAL_TYPE = { by: "internalType", then: COUNT };
|
const INTERNAL_TYPE = { by: "internalType", then: COUNT };
|
||||||
const ALLOCATION_STACK = { by: "allocationStack", then: COUNT, noStack: COUNT };
|
const ALLOCATION_STACK = { by: "allocationStack", then: COUNT, noStack: COUNT };
|
||||||
|
|
|
@ -51,9 +51,19 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let allocationsModel = exports.allocations = PropTypes.shape({
|
||||||
|
// True iff we are recording allocation stacks right now.
|
||||||
|
recording: PropTypes.bool.isRequired,
|
||||||
|
// True iff we are in the process of toggling the recording of allocation
|
||||||
|
// stacks on or off right now.
|
||||||
|
togglingInProgress: PropTypes.bool.isRequired,
|
||||||
|
});
|
||||||
|
|
||||||
let appModel = exports.app = {
|
let appModel = exports.app = {
|
||||||
// {MemoryFront} Used to communicate with platform
|
// {MemoryFront} Used to communicate with platform
|
||||||
front: PropTypes.instanceOf(MemoryFront),
|
front: PropTypes.instanceOf(MemoryFront),
|
||||||
|
// Allocations recording related data.
|
||||||
|
allocations: allocationsModel,
|
||||||
// {HeapAnalysesClient} Used to interface with snapshots
|
// {HeapAnalysesClient} Used to interface with snapshots
|
||||||
heapWorker: PropTypes.instanceOf(HeapAnalysesClient),
|
heapWorker: PropTypes.instanceOf(HeapAnalysesClient),
|
||||||
// The breakdown object DSL describing how we want
|
// The breakdown object DSL describing how we want
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.allocations = require("./reducers/allocations");
|
||||||
exports.snapshots = require("./reducers/snapshots");
|
exports.snapshots = require("./reducers/snapshots");
|
||||||
exports.breakdown = require("./reducers/breakdown");
|
exports.breakdown = require("./reducers/breakdown");
|
||||||
exports.errors = require("./reducers/errors");
|
exports.errors = require("./reducers/errors");
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const { assert } = require("devtools/shared/DevToolsUtils");
|
||||||
|
const { actions } = require("../constants");
|
||||||
|
|
||||||
|
let handlers = Object.create(null);
|
||||||
|
|
||||||
|
handlers[actions.TOGGLE_RECORD_ALLOCATION_STACKS_START] = function (state, action) {
|
||||||
|
assert(!state.togglingInProgress,
|
||||||
|
"Changing recording state must not be reentrant.");
|
||||||
|
|
||||||
|
return {
|
||||||
|
recording: !state.recording,
|
||||||
|
togglingInProgress: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
handlers[actions.TOGGLE_RECORD_ALLOCATION_STACKS_END] = function (state, action) {
|
||||||
|
assert(state.togglingInProgress,
|
||||||
|
"Should not complete changing recording state if we weren't changing "
|
||||||
|
+ "recording state already.");
|
||||||
|
|
||||||
|
return {
|
||||||
|
recording: state.recording,
|
||||||
|
togglingInProgress: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_ALLOCATIONS_STATE = {
|
||||||
|
recording: false,
|
||||||
|
togglingInProgress: false
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function (state = DEFAULT_ALLOCATIONS_STATE, action) {
|
||||||
|
let handle = handlers[action.type];
|
||||||
|
if (handle) {
|
||||||
|
return handle(state, action);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
|
@ -4,6 +4,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
|
'allocations.js',
|
||||||
'breakdown.js',
|
'breakdown.js',
|
||||||
'errors.js',
|
'errors.js',
|
||||||
'snapshots.js',
|
'snapshots.js',
|
||||||
|
|
|
@ -28,6 +28,8 @@ function initDebugger () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function StubbedMemoryFront () {
|
function StubbedMemoryFront () {
|
||||||
|
this.state = "detached";
|
||||||
|
this.recordingAllocations = false;
|
||||||
this.dbg = initDebugger();
|
this.dbg = initDebugger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +45,14 @@ StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached", Task.asy
|
||||||
return ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
|
return ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
|
||||||
}), "saveHeapSnapshot");
|
}), "saveHeapSnapshot");
|
||||||
|
|
||||||
|
StubbedMemoryFront.prototype.startRecordingAllocations = expectState("attached", Task.async(function* () {
|
||||||
|
this.recordingAllocations = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
StubbedMemoryFront.prototype.stopRecordingAllocations = expectState("attached", Task.async(function* () {
|
||||||
|
this.recordingAllocations = false;
|
||||||
|
}));
|
||||||
|
|
||||||
function waitUntilState (store, predicate) {
|
function waitUntilState (store, predicate) {
|
||||||
let deferred = promise.defer();
|
let deferred = promise.defer();
|
||||||
let unsubscribe = store.subscribe(check);
|
let unsubscribe = store.subscribe(check);
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the action creator `setBreakdown()` for breakdown changing.
|
||||||
|
* Does not test refreshing the census information, check `setBreakdownAndRefresh` action
|
||||||
|
* for that.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
equal(getState().allocations.recording, false, "not recording by default");
|
||||||
|
equal(getState().allocations.togglingInProgress, false,
|
||||||
|
"not in the process of toggling by default");
|
||||||
|
|
||||||
|
dispatch(toggleRecordingAllocationStacks(front));
|
||||||
|
yield waitUntilState(store, () => getState().allocations.togglingInProgress);
|
||||||
|
ok(true, "`togglingInProgress` set to true when toggling on");
|
||||||
|
yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
|
||||||
|
|
||||||
|
equal(getState().allocations.recording, true, "now we are recording");
|
||||||
|
ok(front.recordingAllocations, "front is recording too");
|
||||||
|
|
||||||
|
dispatch(toggleRecordingAllocationStacks(front));
|
||||||
|
yield waitUntilState(store, () => getState().allocations.togglingInProgress);
|
||||||
|
ok(true, "`togglingInProgress` set to true when toggling off");
|
||||||
|
yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
|
||||||
|
|
||||||
|
equal(getState().allocations.recording, false, "now we are not recording");
|
||||||
|
ok(front.recordingAllocations, "front is not recording anymore");
|
||||||
|
});
|
|
@ -5,6 +5,7 @@ tail =
|
||||||
firefox-appdir = browser
|
firefox-appdir = browser
|
||||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||||
|
|
||||||
|
[test_action-toggle-recording-allocations.js]
|
||||||
[test_action-select-snapshot.js]
|
[test_action-select-snapshot.js]
|
||||||
[test_action-set-breakdown.js]
|
[test_action-set-breakdown.js]
|
||||||
[test_action-set-breakdown-and-refresh-01.js]
|
[test_action-set-breakdown-and-refresh-01.js]
|
||||||
|
|
Загрузка…
Ссылка в новой задаче