Bug 1214066 - Add the ability to toggle allocation stack recording; r=jsantell

This commit is contained in:
Nick Fitzgerald 2015-10-23 15:18:55 -07:00
Родитель 89efe3149f
Коммит 554bd61388
13 изменённых файлов: 182 добавлений и 6 удалений

Просмотреть файл

@ -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/.
DevToolsModules(
'allocations.js',
'breakdown.js',
'snapshot.js',
)

Просмотреть файл

@ -122,4 +122,3 @@ const selectSnapshot = exports.selectSnapshot = function (snapshot) {
snapshot
};
};

Просмотреть файл

@ -4,8 +4,9 @@
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
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 { selectSnapshotAndRefresh, takeSnapshotAndCensus } = require("./actions/snapshot");
const { breakdownNameToSpec, getBreakdownDisplayData } = require("./utils");
const Toolbar = createFactory(require("./components/toolbar"));
const List = createFactory(require("./components/list"));
@ -31,7 +32,15 @@ const App = createClass({
},
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);
return (
@ -42,6 +51,9 @@ const App = createClass({
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
onBreakdownChange: breakdown =>
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),
onToggleRecordAllocationStacks: () =>
dispatch(toggleRecordingAllocationStacks(front)),
allocations
}),
dom.div({ id: "memory-tool-container" }, [
@ -66,7 +78,10 @@ const App = createClass({
* and passed to components.
*/
function mapStateToProps (state) {
return { snapshots: state.snapshots };
return {
allocations: state.allocations,
snapshots: state.snapshots
};
}
module.exports = connect(mapStateToProps)(App);

Просмотреть файл

@ -4,6 +4,8 @@
const { DOM, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const models = require("../models");
const Toolbar = module.exports = createClass({
displayName: "toolbar",
propTypes: {
@ -13,17 +15,38 @@ const Toolbar = module.exports = createClass({
})).isRequired,
onTakeSnapshotClick: PropTypes.func.isRequired,
onBreakdownChange: PropTypes.func.isRequired,
onToggleRecordAllocationStacks: PropTypes.func.isRequired,
allocations: models.allocations
},
render() {
let { onTakeSnapshotClick, onBreakdownChange, breakdowns } = this.props;
let {
onTakeSnapshotClick,
onBreakdownChange,
breakdowns,
onToggleRecordAllocationStacks,
allocations,
} = this.props;
return (
DOM.div({ className: "devtools-toolbar" }, [
DOM.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
DOM.select({
className: `select-breakdown`,
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_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.
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 INTERNAL_TYPE = { by: "internalType", then: 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 = {
// {MemoryFront} Used to communicate with platform
front: PropTypes.instanceOf(MemoryFront),
// Allocations recording related data.
allocations: allocationsModel,
// {HeapAnalysesClient} Used to interface with snapshots
heapWorker: PropTypes.instanceOf(HeapAnalysesClient),
// 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
* 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";
exports.allocations = require("./reducers/allocations");
exports.snapshots = require("./reducers/snapshots");
exports.breakdown = require("./reducers/breakdown");
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/.
DevToolsModules(
'allocations.js',
'breakdown.js',
'errors.js',
'snapshots.js',

Просмотреть файл

@ -28,6 +28,8 @@ function initDebugger () {
}
function StubbedMemoryFront () {
this.state = "detached";
this.recordingAllocations = false;
this.dbg = initDebugger();
}
@ -43,6 +45,14 @@ StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached", Task.asy
return ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
}), "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) {
let deferred = promise.defer();
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
skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-toggle-recording-allocations.js]
[test_action-select-snapshot.js]
[test_action-set-breakdown.js]
[test_action-set-breakdown-and-refresh-01.js]