Bug 1249779 - Remove the "invert tree" checkbox and make inverting a property of the "group by" selection; r=jsantell

Effectively, this moves `inverted` from a global and per-census state, to a
per-`breakdownModel` state. Additionally, a `breakdownModel` is not just a
breakdown spec anymore, but also some extra associated metadata. As such, it has
been split in two and renamed to "censusDisplayModel" and
"dominatorTreeDisplayModel".

--HG--
rename : devtools/client/memory/reducers/breakdown.js => devtools/client/memory/reducers/census-display.js
rename : devtools/client/memory/reducers/dominatorTreeBreakdown.js => devtools/client/memory/reducers/dominator-tree-display.js
rename : devtools/client/memory/test/browser/browser_memory_allocationStackBreakdown_01.js => devtools/client/memory/test/browser/browser_memory_allocationStackDisplay_01.js
rename : devtools/client/memory/test/browser/browser_memory_breakdowns_01.js => devtools/client/memory/test/browser/browser_memory_displays_01.js
rename : devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-02.js => devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js
This commit is contained in:
Nick Fitzgerald 2016-03-01 16:20:45 -08:00
Родитель 356fd48c1d
Коммит a9e7879204
60 изменённых файлов: 763 добавлений и 1019 удалений

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

@ -48,14 +48,6 @@ aggregate.mb=%S MB
# it has a creation time to display.
snapshot-title.loading=Processing…
# LOCALIZATION NOTE (checkbox.invertTree): The label describing the boolean
# checkbox whether or not to invert the tree.
checkbox.invertTree=Invert tree
# LOCALIZATION NOTE (checkbox.invertTree): The tooltip for the label describing
# the boolean checkbox whether or not to invert the tree.
checkbox.invertTree.tooltip=When uninverted, the tree is shown top-down, giving an overview of memory consumption. When inverted, the tree is shown bottom-up, highlighting the heaviest memory consumers.
# LOCALIZATION NOTE (checkbox.recordAllocationStacks): The label describing the
# boolean checkbox whether or not to record allocation stacks.
checkbox.recordAllocationStacks=Record allocation stacks
@ -65,29 +57,33 @@ checkbox.recordAllocationStacks=Record allocation stacks
# stacks.
checkbox.recordAllocationStacks.tooltip=Toggle the recording of allocation stacks. Subsequent heap snapshots will be able to label and group objects created when allocation stack recording is active by their allocation stack. Recording allocation stacks has a performance overhead.
# LOCALIZATION NOTE (toolbar.breakdownBy): The label describing the select menu
# options of the breakdown options.
toolbar.breakdownBy=Group by:
# LOCALIZATION NOTE (toolbar.displayBy): The label describing the select menu
# options of the display options.
toolbar.displayBy=Group by:
# LOCALIZATION NOTE (toolbar.breakdownBy): The tooltip for the label describing
# the select menu options of the breakdown options.
toolbar.breakdownBy.tooltip=Change how objects are grouped
# LOCALIZATION NOTE (toolbar.displayBy.tooltip): The tooltip for the label
# describing the select menu options of the display options.
toolbar.displayBy.tooltip=Change how objects are grouped
# LOCALIZATION NOTE (breakdowns.coarseType.tooltip): The tooltip for the "coarse
# type" breakdown option.
breakdowns.coarseType.tooltip=Group items into broad categories
# LOCALIZATION NOTE (censusDisplays.coarseType.tooltip): The tooltip for the
# "coarse type" display option.
censusDisplays.coarseType.tooltip=Group items by their type
# LOCALIZATION NOTE (breakdowns.allocationStack.tooltip): The tooltip for the
# "allocation stack" breakdown option.
breakdowns.allocationStack.tooltip=Group items by the JavaScript stack recorded when the object was allocated
# LOCALIZATION NOTE (censusDisplays.allocationStack.tooltip): The tooltip for
# the "allocation stack" display option.
censusDisplays.allocationStack.tooltip=Group items by the JavaScript stack recorded when the object was allocated
# LOCALIZATION NOTE (breakdowns.objectClass.tooltip): The tooltip for the
# "object class" breakdown option.
breakdowns.objectClass.tooltip=Group items by their JavaScript Object [[class]] name
# LOCALIZATION NOTE (censusDisplays.invertedAllocationStack.tooltip): The
# tooltip for the "inverted allocation stack" display option.
censusDisplays.invertedAllocationStack.tooltip=Group items by the inverted JavaScript call stack recorded when the object was created
# LOCALIZATION NOTE (breakdowns.internalType.tooltip): The tooltip for the
# "internal type" breakdown option.
breakdowns.internalType.tooltip=Group items by their internal C++ type
# LOCALIZATION NOTE (censusDisplays.objectClass.tooltip): The tooltip for the
# "object class" display option.
censusDisplays.objectClass.tooltip=Group items by their JavaScript Object [[class]] name
# LOCALIZATION NOTE (censusDisplays.internalType.tooltip): The tooltip for the
# "internal type" display option.
censusDisplays.internalType.tooltip=Group items by their internal C++ type
# LOCALIZATION NOTE (toolbar.labelBy): The label describing the select menu
# options of the label options.
@ -97,17 +93,17 @@ toolbar.labelBy=Label by:
# select menu options of the label options.
toolbar.labelBy.tooltip=Change how objects are labeled
# LOCALIZATION NOTE (dominatorTreeBreakdowns.coarseType.tooltip): The tooltip for the "coarse
# type" dominator tree breakdown option.
dominatorTreeBreakdowns.coarseType.tooltip=Label objects by the broad categories they fit in
# LOCALIZATION NOTE (dominatorTreeDisplays.coarseType.tooltip): The tooltip for
# the "coarse type" dominator tree display option.
dominatorTreeDisplays.coarseType.tooltip=Label objects by the broad categories they fit in
# LOCALIZATION NOTE (dominatorTreeBreakdowns.allocationStack.tooltip): The
# tooltip for the "allocation stack" dominator tree breakdown option.
dominatorTreeBreakdowns.allocationStack.tooltip=Label objects by the JavaScript stack recorded when it was allocated
# LOCALIZATION NOTE (dominatorTreeDisplays.allocationStack.tooltip): The
# tooltip for the "allocation stack" dominator tree display option.
dominatorTreeDisplays.allocationStack.tooltip=Label objects by the JavaScript stack recorded when it was allocated
# LOCALIZATION NOTE (dominatorTreeBreakdowns.internalType.tooltip): The
# tooltip for the "internal type" dominator tree breakdown option.
dominatorTreeBreakdowns.internalType.tooltip=Label objects by their internal C++ type name
# LOCALIZATION NOTE (dominatorTreeDisplays.internalType.tooltip): The
# tooltip for the "internal type" dominator tree display option.
dominatorTreeDisplays.internalType.tooltip=Label objects by their internal C++ type name
# LOCALIZATION NOTE (toolbar.view): The label for the view selector in the
# toolbar.
@ -301,8 +297,8 @@ snapshot.state.saving-census=Saving census…
snapshot.state.error=Error
# LOCALIZATION NOTE (heapview.noAllocationStacks): The message displayed to
# users when selecting a breakdown by "allocation stack" but no allocation
# stacks were recorded in the heap snapshot.
# users when selecting a display by "allocation stack" but no allocation stacks
# were recorded in the heap snapshot.
heapview.noAllocationStacks=No allocation stacks found. Record allocation stacks before taking a heap snapshot.
# LOCALIZATION NOTE (heapview.field.retainedSize): The name of the column in the

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

@ -1,35 +0,0 @@
/* 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 } = require("devtools/shared/DevToolsUtils");
const { breakdownEquals, createSnapshot } = require("../utils");
const { actions, snapshotState: states } = require("../constants");
const { refresh } = require("./refresh");
const setBreakdownAndRefresh = exports.setBreakdownAndRefresh = function (heapWorker, breakdown) {
return function *(dispatch, getState) {
// Clears out all stored census data and sets the breakdown.
dispatch(setBreakdown(breakdown));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all census data in the snapshots and sets
* a new breakdown.
*
* @param {Breakdown} breakdown
*/
const setBreakdown = exports.setBreakdown = function (breakdown) {
assert(typeof breakdown === "object"
&& breakdown
&& breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(breakdown)}`);
return {
type: actions.SET_BREAKDOWN,
breakdown,
};
};

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

@ -0,0 +1,34 @@
/* 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 } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");
exports.setCensusDisplayAndRefresh = function(heapWorker, display) {
return function*(dispatch, getState) {
dispatch(setCensusDisplay(display));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all cached census data in the snapshots and sets new display data
* for censuses.
*
* @param {censusDisplayModel} display
*/
const setCensusDisplay = exports.setCensusDisplay = function(display) {
assert(typeof display === "object"
&& display
&& display.breakdown
&& display.breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(display)}`);
return {
type: actions.SET_CENSUS_DISPLAY,
display,
};
};

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

@ -7,7 +7,6 @@ const { assert, reportException } = require("devtools/shared/DevToolsUtils");
const { actions, diffingState, viewState } = require("../constants");
const telemetry = require("../telemetry");
const {
breakdownEquals,
getSnapshot,
censusIsUpToDate,
snapshotIsDiffable
@ -51,11 +50,10 @@ const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, sec
`Second snapshot must be in a diffable state, found ${second.state}`);
let report, parentMap;
let inverted = getState().inverted;
let breakdown = getState().breakdown;
let display = getState().censusDisplay;
let filter = getState().filter;
if (censusIsUpToDate(inverted, filter, breakdown, getState().diffing.census)) {
if (censusIsUpToDate(filter, display, getState().diffing.census)) {
return;
}
@ -70,36 +68,36 @@ const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, sec
return;
}
inverted = getState().inverted;
breakdown = getState().breakdown;
display = getState().censusDisplay;
filter = getState().filter;
dispatch({
type: actions.TAKE_CENSUS_DIFF_START,
first,
second,
inverted,
filter,
breakdown,
display,
});
let opts = inverted ? { asInvertedTreeNode: true } : { asTreeNode: true };
let opts = display.inverted
? { asInvertedTreeNode: true }
: { asTreeNode: true };
opts.filter = filter || null;
try {
({ delta: report, parentMap } = yield heapWorker.takeCensusDiff(first.path,
second.path,
{ breakdown },
opts));
({ delta: report, parentMap } = yield 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 (inverted !== getState().inverted
|| filter !== getState().filter
|| !breakdownEquals(breakdown, getState().breakdown));
while (filter !== getState().filter
|| display !== getState().censusDisplay);
dispatch({
type: actions.TAKE_CENSUS_DIFF_END,
@ -107,19 +105,18 @@ const takeCensusDiff = exports.takeCensusDiff = function (heapWorker, first, sec
second,
report,
parentMap,
inverted,
filter,
breakdown,
display,
});
telemetry.countDiff({ inverted, filter, breakdown });
telemetry.countDiff({ filter, display });
};
};
/**
* Ensure that the current diffing data is up to date with the currently
* selected breakdown, filter, inversion, etc. If the state is not up-to-date,
* then a recompute is triggered.
* selected display, filter, etc. If the state is not up-to-date, then a
* recompute is triggered.
*
* @param {HeapAnalysesClient} heapWorker
*/

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

@ -0,0 +1,35 @@
/* 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 } = require("devtools/shared/DevToolsUtils");
const { actions } = require("../constants");
const { refresh } = require("./refresh");
exports.setDominatorTreeDisplayAndRefresh = function(heapWorker, display) {
return function*(dispatch, getState) {
// Clears out all stored census data and sets the display.
dispatch(setDominatorTreeDisplay(display));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all census data in the snapshots and sets
* a new display.
*
* @param {dominatorTreeDisplayModel} display
*/
const setDominatorTreeDisplay = exports.setDominatorTreeDisplay = function (display) {
assert(typeof display === "object"
&& display
&& display.breakdown
&& display.breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(display)}`);
return {
type: actions.SET_DOMINATOR_TREE_DISPLAY,
display,
};
};

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

@ -1,37 +0,0 @@
/* 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 } = require("devtools/shared/DevToolsUtils");
const { breakdownEquals, createSnapshot } = require("../utils");
const { actions, snapshotState: states } = require("../constants");
const { refresh } = require("./refresh");
const setDominatorTreeBreakdownAndRefresh =
exports.setDominatorTreeBreakdownAndRefresh =
function (heapWorker, breakdown) {
return function *(dispatch, getState) {
// Clears out all stored census data and sets the breakdown.
dispatch(setDominatorTreeBreakdown(breakdown));
yield dispatch(refresh(heapWorker));
};
};
/**
* Clears out all census data in the snapshots and sets
* a new breakdown.
*
* @param {Breakdown} breakdown
*/
const setDominatorTreeBreakdown = exports.setDominatorTreeBreakdown = function (breakdown) {
assert(typeof breakdown === "object"
&& breakdown
&& breakdown.by,
`Breakdowns must be an object with a \`by\` property, attempted to set: ${uneval(breakdown)}`);
return {
type: actions.SET_DOMINATOR_TREE_BREAKDOWN,
breakdown,
};
};

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

@ -1,18 +0,0 @@
/* 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 } = require("../constants");
const { refresh } = require("./refresh");
const toggleInverted = exports.toggleInverted = function () {
return { type: actions.TOGGLE_INVERTED };
};
exports.toggleInvertedAndRefresh = function (heapWorker) {
return function* (dispatch, getState) {
dispatch(toggleInverted());
yield dispatch(refresh(heapWorker));
};
};

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

@ -5,11 +5,10 @@
DevToolsModules(
'allocations.js',
'breakdown.js',
'census-display.js',
'diffing.js',
'dominatorTreeBreakdown.js',
'dominator-tree-display.js',
'filter.js',
'inverted.js',
'io.js',
'refresh.js',
'sizes.js',

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

@ -7,7 +7,6 @@ const { assert, reportException } = require("devtools/shared/DevToolsUtils");
const {
censusIsUpToDate,
getSnapshot,
breakdownEquals,
createSnapshot,
dominatorTreeIsComputed,
} = require("../utils");
@ -44,7 +43,7 @@ const takeSnapshotAndCensus = exports.takeSnapshotAndCensus = function (front, h
/**
* Selects a snapshot and if the snapshot's census is using a different
* breakdown, take a new census.
* display, take a new census.
*
* @param {HeapAnalysesClient} heapWorker
* @param {snapshotId} id
@ -137,65 +136,63 @@ const takeCensus = exports.takeCensus = function (heapWorker, id) {
`Can only take census of snapshots in READ or SAVED_CENSUS state, found ${snapshot.state}`);
let report, parentMap;
let inverted = getState().inverted;
let breakdown = getState().breakdown;
let display = getState().censusDisplay;
let filter = getState().filter;
// If breakdown, filter and inversion haven't changed, don't do anything.
if (censusIsUpToDate(inverted, filter, breakdown, snapshot.census)) {
// If display, filter and inversion haven't changed, don't do anything.
if (censusIsUpToDate(filter, display, snapshot.census)) {
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.
// Keep taking a census if the display changes while our request is in
// flight. Recheck that the display used for the census is the same as the
// state's display.
do {
inverted = getState().inverted;
breakdown = getState().breakdown;
display = getState().censusDisplay;
filter = getState().filter;
dispatch({
type: actions.TAKE_CENSUS_START,
id,
inverted,
filter,
breakdown
display
});
let opts = inverted ? { asInvertedTreeNode: true } : { asTreeNode: true };
let opts = display.inverted
? { asInvertedTreeNode: true }
: { asTreeNode: true };
opts.filter = filter || null;
try {
({ report, parentMap } = yield heapWorker.takeCensus(snapshot.path,
{ breakdown },
opts));
({ report, parentMap } = yield heapWorker.takeCensus(
snapshot.path,
{ breakdown: display.breakdown },
opts));
} catch (error) {
reportException("takeCensus", error);
dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
return;
}
}
while (inverted !== getState().inverted ||
filter !== getState().filter ||
!breakdownEquals(breakdown, getState().breakdown));
while (filter !== getState().filter ||
display !== getState().censusDisplay);
dispatch({
type: actions.TAKE_CENSUS_END,
id,
breakdown,
inverted,
display,
filter,
report,
parentMap
});
telemetry.countCensus({ inverted, filter, breakdown });
telemetry.countCensus({ filter, display });
};
};
/**
* Refresh the selected snapshot's census data, if need be (for example,
* breakdown configuration changed).
* display configuration changed).
*
* @param {HeapAnalysesClient} heapWorker
*/
@ -261,18 +258,19 @@ const fetchDominatorTree = exports.fetchDominatorTree = function (heapWorker, id
assert(dominatorTreeIsComputed(snapshot),
"Should have dominator tree model and it should be computed");
let breakdown;
let display;
let root;
do {
breakdown = getState().dominatorTreeBreakdown;
assert(breakdown, "Should have a breakdown to describe nodes with.");
display = getState().dominatorTreeDisplay;
assert(display && display.breakdown,
`Should have a breakdown to describe nodes with, got: ${uneval(display)}`);
dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, breakdown });
dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, display });
try {
root = yield heapWorker.getDominatorTree({
dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
breakdown,
breakdown: display.breakdown,
});
} catch (error) {
reportException("actions/snapshot/fetchDominatorTree", error);
@ -280,10 +278,10 @@ const fetchDominatorTree = exports.fetchDominatorTree = function (heapWorker, id
return null;
}
}
while (!breakdownEquals(breakdown, getState().dominatorTreeBreakdown));
while (display !== getState().dominatorTreeDisplay);
dispatch({ type: actions.FETCH_DOMINATOR_TREE_END, id, root });
telemetry.countDominatorTree({ breakdown });
telemetry.countDominatorTree({ display });
return root;
};
};
@ -305,18 +303,18 @@ const fetchImmediatelyDominated = exports.fetchImmediatelyDominated = function (
"Cannot fetch immediately dominated nodes in a dominator tree unless " +
" the dominator tree has already been computed");
let breakdown;
let display;
let response;
do {
breakdown = getState().dominatorTreeBreakdown;
assert(breakdown, "Should have a breakdown to describe nodes with.");
display = getState().dominatorTreeDisplay;
assert(display, "Should have a display to describe nodes with.");
dispatch({ type: actions.FETCH_IMMEDIATELY_DOMINATED_START, id });
try {
response = yield heapWorker.getImmediatelyDominated({
dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
breakdown,
breakdown: display.breakdown,
nodeId: lazyChildren.parentNodeId(),
startIndex: lazyChildren.siblingIndex(),
});
@ -326,7 +324,7 @@ const fetchImmediatelyDominated = exports.fetchImmediatelyDominated = function (
return null;
}
}
while (!breakdownEquals(breakdown, getState().dominatorTreeBreakdown));
while (display !== getState().dominatorTreeDisplay);
dispatch({
type: actions.FETCH_IMMEDIATELY_DOMINATED_END,

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

@ -6,10 +6,14 @@ const { assert } = require("devtools/shared/DevToolsUtils");
const { appinfo } = require("Services");
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { breakdowns, diffingState, viewState } = require("./constants");
const { censusDisplays, dominatorTreeDisplays, diffingState, viewState } = require("./constants");
const { toggleRecordingAllocationStacks } = require("./actions/allocations");
const { setBreakdownAndRefresh } = require("./actions/breakdown");
const { setDominatorTreeBreakdownAndRefresh } = require("./actions/dominatorTreeBreakdown");
const { setCensusDisplayAndRefresh } = require("./actions/census-display");
const { setDominatorTreeDisplayAndRefresh } = require("./actions/dominator-tree-display");
const {
getCustomCensusDisplays,
getCustomDominatorTreeDisplays,
} = require("devtools/client/memory/utils");
const {
selectSnapshotForDiffingAndRefresh,
toggleDiffing,
@ -17,7 +21,6 @@ const {
collapseDiffingCensusNode,
focusDiffingCensusNode,
} = require("./actions/diffing");
const { toggleInvertedAndRefresh } = require("./actions/inverted");
const { setFilterStringAndRefresh } = require("./actions/filter");
const { pickFileAndExportSnapshot, pickFileAndImportSnapshotAndCensus } = require("./actions/io");
const {
@ -34,12 +37,6 @@ const {
} = require("./actions/snapshot");
const { changeViewAndRefresh } = require("./actions/view");
const { resizeShortestPaths } = require("./actions/sizes");
const {
breakdownNameToSpec,
getBreakdownDisplayData,
dominatorTreeBreakdownNameToSpec,
getDominatorTreeBreakdownDisplayData,
} = require("./utils");
const Toolbar = createFactory(require("./components/toolbar"));
const List = createFactory(require("./components/list"));
const SnapshotListItem = createFactory(require("./components/snapshot-list-item"));
@ -87,12 +84,14 @@ const MemoryApp = createClass({
let isOSX = appinfo.OS == "Darwin";
let isAccelKey = (isOSX && e.metaKey) || (!isOSX && e.ctrlKey);
// On ACCEL+UP, select previous snapshot.
if (isAccelKey && e.key === "ArrowUp") {
let previousIndex = Math.max(0, selectedIndex - 1);
let previousSnapshotId = snapshots[previousIndex].id;
dispatch(selectSnapshotAndRefresh(heapWorker, previousSnapshotId));
}
// On ACCEL+DOWN, select next snapshot.
if (isAccelKey && e.key === "ArrowDown") {
let nextIndex = Math.min(snapshots.length - 1, selectedIndex + 1);
@ -101,15 +100,40 @@ const MemoryApp = createClass({
}
},
_getCensusDisplays() {
const customDisplays = getCustomCensusDisplays();
const custom = Object.keys(customDisplays).reduce((arr, key) => {
arr.push(customDisplays[key]);
return arr;
}, []);
return [
censusDisplays.coarseType,
censusDisplays.allocationStack,
censusDisplays.invertedAllocationStack,
].concat(custom);
},
_getDominatorTreeDisplays() {
const customDisplays = getCustomDominatorTreeDisplays();
const custom = Object.keys(customDisplays).reduce((arr, key) => {
arr.push(customDisplays[key]);
return arr;
}, []);
return [
dominatorTreeDisplays.coarseType,
dominatorTreeDisplays.allocationStack,
].concat(custom);
},
render() {
let {
dispatch,
snapshots,
front,
heapWorker,
breakdown,
allocations,
inverted,
toolbox,
filter,
diffing,
@ -131,30 +155,24 @@ const MemoryApp = createClass({
Toolbar({
snapshots,
breakdowns: getBreakdownDisplayData(),
censusDisplays: this._getCensusDisplays(),
onCensusDisplayChange: newDisplay =>
dispatch(setCensusDisplayAndRefresh(heapWorker, newDisplay)),
onImportClick: () => dispatch(pickFileAndImportSnapshotAndCensus(heapWorker)),
onClearSnapshotsClick: () => dispatch(clearSnapshots(heapWorker)),
onTakeSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
onBreakdownChange: breakdown =>
dispatch(setBreakdownAndRefresh(heapWorker, breakdownNameToSpec(breakdown))),
onToggleRecordAllocationStacks: () =>
dispatch(toggleRecordingAllocationStacks(front)),
allocations,
inverted,
onToggleInverted: () =>
dispatch(toggleInvertedAndRefresh(heapWorker)),
filterString: filter,
setFilterString: filterString =>
dispatch(setFilterStringAndRefresh(filterString, heapWorker)),
diffing,
onToggleDiffing: () => dispatch(toggleDiffing()),
view,
dominatorTreeBreakdowns: getDominatorTreeBreakdownDisplayData(),
onDominatorTreeBreakdownChange: breakdown => {
const spec = dominatorTreeBreakdownNameToSpec(breakdown);
assert(spec, "Should have a breakdown spec");
dispatch(setDominatorTreeBreakdownAndRefresh(heapWorker, spec));
},
dominatorTreeDisplays: this._getDominatorTreeDisplays(),
onDominatorTreeDisplayChange: newDisplay =>
dispatch(setDominatorTreeDisplayAndRefresh(heapWorker, newDisplay)),
onViewChange: v => dispatch(changeViewAndRefresh(v, heapWorker)),
}),

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

@ -66,7 +66,7 @@ const Census = module.exports = createClass({
getPercentBytes,
getPercentCount,
showSign: !!diffing,
inverted: census.inverted,
inverted: census.display.inverted,
}),
getRoots: () => report.children || [],
getKey: node => node.id,

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

@ -258,7 +258,7 @@ const Heap = module.exports = createClass({
_renderCensus(state, census, diffing, onViewSourceInDebugger) {
const contents = [];
if (census.breakdown.by === "allocationStack"
if (census.display.breakdown.by === "allocationStack"
&& census.report
&& census.report.children
&& census.report.children.length === 1

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

@ -1,6 +1,7 @@
/* 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 } = require("devtools/shared/DevToolsUtils");
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
@ -8,32 +9,30 @@ const { L10N } = require("../utils");
const models = require("../models");
const { viewState } = require("../constants");
const Toolbar = module.exports = createClass({
module.exports = createClass({
displayName: "Toolbar",
propTypes: {
breakdowns: PropTypes.arrayOf(PropTypes.shape({
censusDisplays: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
})).isRequired,
onTakeSnapshotClick: PropTypes.func.isRequired,
onImportClick: PropTypes.func.isRequired,
onClearSnapshotsClick: PropTypes.func.isRequired,
onBreakdownChange: PropTypes.func.isRequired,
onCensusDisplayChange: PropTypes.func.isRequired,
onToggleRecordAllocationStacks: PropTypes.func.isRequired,
allocations: models.allocations,
onToggleInverted: PropTypes.func.isRequired,
inverted: PropTypes.bool.isRequired,
filterString: PropTypes.string,
setFilterString: PropTypes.func.isRequired,
diffing: models.diffingModel,
onToggleDiffing: PropTypes.func.isRequired,
view: PropTypes.string.isRequired,
onViewChange: PropTypes.func.isRequired,
dominatorTreeBreakdowns: PropTypes.arrayOf(PropTypes.shape({
dominatorTreeDisplays: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
})).isRequired,
onDominatorTreeBreakdownChange: PropTypes.func.isRequired,
onDominatorTreeDisplayChange: PropTypes.func.isRequired,
snapshots: PropTypes.arrayOf(models.snapshot).isRequired,
},
@ -42,14 +41,12 @@ const Toolbar = module.exports = createClass({
onTakeSnapshotClick,
onImportClick,
onClearSnapshotsClick,
onBreakdownChange,
breakdowns,
dominatorTreeBreakdowns,
onDominatorTreeBreakdownChange,
onCensusDisplayChange,
censusDisplays,
dominatorTreeDisplays,
onDominatorTreeDisplayChange,
onToggleRecordAllocationStacks,
allocations,
onToggleInverted,
inverted,
filterString,
setFilterString,
snapshots,
@ -68,20 +65,24 @@ const Toolbar = module.exports = createClass({
dom.label(
{
className: "breakdown-by",
title: L10N.getStr("toolbar.breakdownBy.tooltip"),
className: "display-by",
title: L10N.getStr("toolbar.displayBy.tooltip"),
},
L10N.getStr("toolbar.breakdownBy"),
L10N.getStr("toolbar.displayBy"),
dom.select(
{
id: "select-breakdown",
className: "select-breakdown",
onChange: e => onBreakdownChange(e.target.value),
id: "select-display",
className: "select-display",
onChange: e => {
const newDisplay =
censusDisplays.find(b => b.displayName === e.target.value);
onCensusDisplayChange(newDisplay);
},
},
breakdowns.map(({ name, tooltip, displayName }) => dom.option(
censusDisplays.map(({ tooltip, displayName }) => dom.option(
{
key: name,
value: name,
key: `display-${displayName}`,
value: displayName,
title: tooltip,
},
displayName
@ -89,19 +90,6 @@ const Toolbar = module.exports = createClass({
)
),
dom.label(
{
title: L10N.getStr("checkbox.invertTree.tooltip")
},
dom.input({
id: "invert-tree-checkbox",
type: "checkbox",
checked: inverted,
onChange: onToggleInverted,
}),
L10N.getStr("checkbox.invertTree")
),
dom.div({ id: "toolbar-spacer", className: "spacer" }),
dom.input({
@ -111,7 +99,7 @@ const Toolbar = module.exports = createClass({
placeholder: L10N.getStr("filter.placeholder"),
title: L10N.getStr("filter.tooltip"),
onChange: event => setFilterString(event.target.value),
value: !!filterString ? filterString : undefined,
value: filterString || undefined,
})
);
} else {
@ -130,13 +118,17 @@ const Toolbar = module.exports = createClass({
L10N.getStr("toolbar.labelBy"),
dom.select(
{
id: "select-dominator-tree-breakdown",
onChange: e => onDominatorTreeBreakdownChange(e.target.value),
id: "select-dominator-tree-display",
onChange: e => {
const newDisplay =
dominatorTreeDisplays.find(b => b.displayName === e.target.value);
onDominatorTreeDisplayChange(newDisplay);
},
},
dominatorTreeBreakdowns.map(({ name, tooltip, displayName }) => dom.option(
dominatorTreeDisplays.map(({ tooltip, displayName }) => dom.option(
{
key: name,
value: name,
key: `dominator-tree-display-${displayName}`,
value: displayName,
title: tooltip,
},
displayName

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

@ -65,11 +65,11 @@ actions.TAKE_CENSUS_DIFF_START = "take-census-diff-start";
actions.TAKE_CENSUS_DIFF_END = "take-census-diff-end";
actions.DIFFING_ERROR = "diffing-error";
// Fired to set a new breakdown.
actions.SET_BREAKDOWN = "set-breakdown";
// Fired to set a new census display.
actions.SET_CENSUS_DISPLAY = "set-census-display";
// Fired to change the breakdown that controls the dominator tree labels.
actions.SET_DOMINATOR_TREE_BREAKDOWN = "set-dominator-tree-breakdown";
// Fired to change the display that controls the dominator tree labels.
actions.SET_DOMINATOR_TREE_DISPLAY = "set-dominator-tree-display";
// Fired when changing between census or dominators view.
actions.CHANGE_VIEW = "change-view";
@ -103,23 +103,24 @@ actions.COLLAPSE_DOMINATOR_TREE_NODE = "collapse-dominator-tree-node";
actions.RESIZE_SHORTEST_PATHS = "resize-shortest-paths";
/*** Breakdowns ***************************************************************/
/*** Census Displays ***************************************************************/
const COUNT = { by: "count", count: true, bytes: true };
const INTERNAL_TYPE = { by: "internalType", then: COUNT };
const ALLOCATION_STACK = { by: "allocationStack", then: COUNT, noStack: COUNT };
const OBJECT_CLASS = { by: "objectClass", then: COUNT, other: COUNT };
const COUNT = Object.freeze({ by: "count", count: true, bytes: true });
const INTERNAL_TYPE = Object.freeze({ by: "internalType", then: COUNT });
const ALLOCATION_STACK = Object.freeze({ by: "allocationStack", then: COUNT, noStack: COUNT });
const OBJECT_CLASS = Object.freeze({ by: "objectClass", then: COUNT, other: COUNT });
const breakdowns = exports.breakdowns = {
coarseType: {
exports.censusDisplays = Object.freeze({
coarseType: Object.freeze({
displayName: "Type",
get tooltip() {
// Importing down here is necessary because of the circular dependency
// this introduces with `./utils.js`.
const { L10N } = require("./utils");
return L10N.getStr("breakdowns.coarseType.tooltip");
return L10N.getStr("censusDisplays.coarseType.tooltip");
},
breakdown: {
inverted: true,
breakdown: Object.freeze({
by: "coarseType",
objects: OBJECT_CLASS,
strings: COUNT,
@ -129,57 +130,68 @@ const breakdowns = exports.breakdowns = {
noFilename: INTERNAL_TYPE
},
other: INTERNAL_TYPE,
}
},
})
}),
allocationStack: {
allocationStack: Object.freeze({
displayName: "Call Stack",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("breakdowns.allocationStack.tooltip");
return L10N.getStr("censusDisplays.allocationStack.tooltip");
},
inverted: false,
breakdown: ALLOCATION_STACK,
},
};
}),
const DOMINATOR_TREE_LABEL_COARSE_TYPE = {
invertedAllocationStack: Object.freeze({
displayName: "Inverted Call Stack",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("censusDisplays.invertedAllocationStack.tooltip");
},
inverted: true,
breakdown: ALLOCATION_STACK,
}),
});
const DOMINATOR_TREE_LABEL_COARSE_TYPE = Object.freeze({
by: "coarseType",
objects: OBJECT_CLASS,
scripts: {
scripts: Object.freeze({
by: "internalType",
then: {
then: Object.freeze({
by: "filename",
then: COUNT,
noFilename: COUNT,
},
},
}),
}),
strings: INTERNAL_TYPE,
other: INTERNAL_TYPE,
};
});
const dominatorTreeBreakdowns = exports.dominatorTreeBreakdowns = {
coarseType: {
exports.dominatorTreeDisplays = Object.freeze({
coarseType: Object.freeze({
displayName: "Type",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("dominatorTreeBreakdowns.coarseType.tooltip");
return L10N.getStr("dominatorTreeDisplays.coarseType.tooltip");
},
breakdown: DOMINATOR_TREE_LABEL_COARSE_TYPE
},
}),
allocationStack: {
allocationStack: Object.freeze({
displayName: "Call Stack",
get tooltip() {
const { L10N } = require("./utils");
return L10N.getStr("dominatorTreeBreakdowns.allocationStack.tooltip");
return L10N.getStr("dominatorTreeDisplays.allocationStack.tooltip");
},
breakdown: {
breakdown: Object.freeze({
by: "allocationStack",
then: DOMINATOR_TREE_LABEL_COARSE_TYPE,
noStack: DOMINATOR_TREE_LABEL_COARSE_TYPE,
},
},
};
}),
}),
});
/*** View States **************************************************************/

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

@ -45,12 +45,32 @@ function catchAndIgnore(fn) {
}
/**
* The breakdown object DSL describing how we want
* the census data to be.
* The data describing the census report's shape, and its associated metadata.
*
* @see `js/src/doc/Debugger/Debugger.Memory.md`
*/
let breakdownModel = exports.breakdown = PropTypes.shape({
by: PropTypes.string.isRequired,
const censusDisplayModel = exports.censusDisplay = PropTypes.shape({
displayName: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
inverted: PropTypes.bool.isRequired,
breakdown: PropTypes.shape({
by: PropTypes.string.isRequired,
})
});
/**
* How we want to label nodes in the dominator tree, and associated
* metadata. The notable difference from `censusDisplayModel` is the lack of
* an `inverted` property.
*
* @see `js/src/doc/Debugger/Debugger.Memory.md`
*/
const dominatorTreeDisplayModel = exports.dominatorTreeDisplay = PropTypes.shape({
displayName: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
breakdown: PropTypes.shape({
by: PropTypes.string.isRequired,
})
});
let censusModel = exports.censusModel = PropTypes.shape({
@ -58,10 +78,8 @@ let censusModel = exports.censusModel = PropTypes.shape({
report: PropTypes.object,
// The parent map for the report.
parentMap: PropTypes.object,
// The breakdown used to generate the current census
breakdown: breakdownModel,
// Whether the currently cached report tree is inverted or not.
inverted: PropTypes.bool,
// The display data used to generate the current census.
display: censusDisplayModel,
// If present, the currently cached report's filter string used for pruning
// the tree items.
filter: PropTypes.string,
@ -99,9 +117,9 @@ let dominatorTreeModel = exports.dominatorTreeModel = PropTypes.shape({
PropTypes.object,
]),
// The breakdown used to generate descriptive labels of nodes in this
// dominator tree.
breakdown: breakdownModel,
// The display used to generate descriptive labels of nodes in this dominator
// tree.
display: dominatorTreeDisplayModel,
// The number of active requests to incrementally fetch subtrees. This should
// only be non-zero when the state is INCREMENTAL_FETCHING.
@ -197,7 +215,8 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
if (shouldHavePath.includes(current) && !snapshot.path) {
throw new Error(`Snapshots in state ${current} must have a snapshot path.`);
}
if (shouldHaveCensus.includes(current) && (!snapshot.census || !snapshot.census.breakdown)) {
if (shouldHaveCensus.includes(current) &&
(!snapshot.census || !snapshot.census.display || !snapshot.census.display.breakdown)) {
throw new Error(`Snapshots in state ${current} must have a census and breakdown.`);
}
if (shouldHaveCreationTime.includes(current) && !snapshot.creationTime) {
@ -269,22 +288,16 @@ let appModel = exports.app = {
// {HeapAnalysesClient} Used to interface with snapshots
heapWorker: PropTypes.instanceOf(HeapAnalysesClient),
// The breakdown object DSL describing how we want
// the census data to be.
// @see `js/src/doc/Debugger/Debugger.Memory.md`
breakdown: breakdownModel.isRequired,
// The display data describing how we want the census data to be.
censusDisplay: censusDisplayModel.isRequired,
// The breakdown object DSL describing how we want
// the dominator tree labels to be computed.
// @see `js/src/doc/Debugger/Debugger.Memory.md`
dominatorTreeBreakdown: breakdownModel.isRequired,
// The display data describing how we want the dominator tree labels to be
// computed.
dominatorTreeDisplay: dominatorTreeDisplayModel.isRequired,
// List of reference to all snapshots taken
snapshots: PropTypes.arrayOf(snapshotModel).isRequired,
// True iff we want the tree displayed inverted.
inverted: PropTypes.bool.isRequired,
// If present, a filter string for pruning the tree items.
filter: PropTypes.string,

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

@ -4,12 +4,11 @@
"use strict";
exports.allocations = require("./reducers/allocations");
exports.breakdown = require("./reducers/breakdown");
exports.censusDisplay = require("./reducers/census-display");
exports.diffing = require("./reducers/diffing");
exports.dominatorTreeBreakdown = require("./reducers/dominatorTreeBreakdown");
exports.dominatorTreeDisplay = require("./reducers/dominator-tree-display");
exports.errors = require("./reducers/errors");
exports.filter = require("./reducers/filter");
exports.inverted = require("./reducers/inverted");
exports.sizes = require("./reducers/sizes");
exports.snapshots = require("./reducers/snapshots");
exports.view = require("./reducers/view");

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

@ -1,17 +1,18 @@
/* 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, breakdowns } = require("../constants");
const DEFAULT_BREAKDOWN = breakdowns.coarseType.breakdown;
const { actions, censusDisplays } = require("../constants");
const DEFAULT_CENSUS_DISPLAY = censusDisplays.coarseType;
let handlers = Object.create(null);
handlers[actions.SET_BREAKDOWN] = function (_, action) {
return Object.assign({}, action.breakdown);
handlers[actions.SET_CENSUS_DISPLAY] = function(_, { display }) {
return display;
};
module.exports = function (state=DEFAULT_BREAKDOWN, action) {
module.exports = function(state = DEFAULT_CENSUS_DISPLAY, action) {
let handle = handlers[action.type];
if (handle) {
return handle(state, action);

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

@ -65,7 +65,7 @@ handlers[actions.TAKE_CENSUS_DIFF_START] = function (diffing, action) {
report: null,
inverted: action.inverted,
filter: action.filter,
breakdown: action.breakdown,
display: action.display,
}
});
};
@ -85,7 +85,7 @@ handlers[actions.TAKE_CENSUS_DIFF_END] = function (diffing, action) {
expanded: new Set(),
inverted: action.inverted,
filter: action.filter,
breakdown: action.breakdown,
display: action.display,
}
});
};

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

@ -4,16 +4,16 @@
"use strict";
const { actions, dominatorTreeBreakdowns } = require("../constants");
const DEFAULT_BREAKDOWN = dominatorTreeBreakdowns.coarseType.breakdown;
const { actions, dominatorTreeDisplays } = require("../constants");
const DEFAULT_DOMINATOR_TREE_DISPLAY = dominatorTreeDisplays.coarseType;
const handlers = Object.create(null);
handlers[actions.SET_DOMINATOR_TREE_BREAKDOWN] = function (_, { breakdown }) {
return breakdown;
handlers[actions.SET_DOMINATOR_TREE_DISPLAY] = function (_, { display }) {
return display;
};
module.exports = function (state = DEFAULT_BREAKDOWN, action) {
module.exports = function (state = DEFAULT_DOMINATOR_TREE_DISPLAY, action) {
const handler = handlers[action.type];
return handler ? handler(state, action) : state;
};

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

@ -1,14 +0,0 @@
/* 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 } = require("../constants");
module.exports = function (inverted = false, action) {
if (action.type === actions.TOGGLE_INVERTED) {
return !inverted;
} else {
return inverted;
}
};

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

@ -5,12 +5,11 @@
DevToolsModules(
'allocations.js',
'breakdown.js',
'census-display.js',
'diffing.js',
'dominatorTreeBreakdown.js',
'dominator-tree-display.js',
'errors.js',
'filter.js',
'inverted.js',
'sizes.js',
'snapshots.js',
'view.js',

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

@ -52,11 +52,10 @@ handlers[actions.READ_SNAPSHOT_END] = function (snapshots, { id, creationTime })
});
};
handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, breakdown, inverted, filter }) {
handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, display, filter }) {
const census = {
report: null,
breakdown,
inverted,
display,
filter,
};
@ -70,15 +69,13 @@ handlers[actions.TAKE_CENSUS_START] = function (snapshots, { id, breakdown, inve
handlers[actions.TAKE_CENSUS_END] = function (snapshots, { id,
report,
parentMap,
breakdown,
inverted,
display,
filter }) {
const census = {
report,
parentMap,
expanded: new Set(),
breakdown,
inverted,
display,
filter,
};
@ -194,7 +191,7 @@ handlers[actions.COMPUTE_DOMINATOR_TREE_END] = function (snapshots, { id, domina
});
};
handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, breakdown }) {
handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, display }) {
return snapshots.map(snapshot => {
if (snapshot.id !== id) {
return snapshot;
@ -208,7 +205,7 @@ handlers[actions.FETCH_DOMINATOR_TREE_START] = function (snapshots, { id, breakd
const dominatorTree = immutableUpdate(snapshot.dominatorTree, {
state: dominatorTreeState.FETCHING,
root: undefined,
breakdown,
display,
});
return immutableUpdate(snapshot, { dominatorTree });
});

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

@ -10,7 +10,7 @@
const { telemetry } = require("Services");
const { makeInfallible, immutableUpdate } = require("devtools/shared/DevToolsUtils");
const { dominatorTreeBreakdowns, breakdowns } = require("./constants");
const { dominatorTreeDisplays, censusDisplays } = require("./constants");
exports.countTakeSnapshot = makeInfallible(function () {
const histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_TAKE_SNAPSHOT_COUNT");
@ -29,26 +29,22 @@ exports.countExportSnapshot = makeInfallible(function () {
const COARSE_TYPE = "Coarse Type";
const ALLOCATION_STACK = "Allocation Stack";
const OBJECT_CLASS = "Object Class";
const INTERNAL_TYPE = "Internal Type";
const INVERTED_ALLOCATION_STACK = "Inverted Allocation Stack";
const CUSTOM = "Custom";
/**
* @param {Boolean} inverted
* True if the census was inverted, false otherwise.
*
* @param {String|null} filter
* The filter string used, if any.
*
* @param {Boolean} diffing
* True if the census was a diffing census, false otherwise.
*
* @param {Object} breakdown
* The breakdown used with the census.
* @param {censusDisplayModel} display
* The display used with the census.
*/
exports.countCensus = makeInfallible(function ({ inverted, filter, diffing, breakdown }) {
exports.countCensus = makeInfallible(function ({ filter, diffing, display }) {
let histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_INVERTED_CENSUS");
histogram.add(!!inverted);
histogram.add(!!display.inverted);
histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_FILTER_CENSUS");
histogram.add(!!filter);
@ -57,10 +53,12 @@ exports.countCensus = makeInfallible(function ({ inverted, filter, diffing, brea
histogram.add(!!diffing);
histogram = telemetry.getKeyedHistogramById("DEVTOOLS_MEMORY_BREAKDOWN_CENSUS_COUNT");
if (breakdown === breakdowns.coarseType.breakdown) {
if (display === censusDisplays.coarseType) {
histogram.add(COARSE_TYPE);
} else if (breakdown === breakdowns.allocationStack.breakdown) {
} else if (display === censusDisplays.allocationStack) {
histogram.add(ALLOCATION_STACK);
} else if (display === censusDisplays.invertedAllocationStack) {
histogram.add(INVERTED_ALLOCATION_STACK);
} else {
histogram.add(CUSTOM);
}
@ -75,17 +73,17 @@ exports.countDiff = makeInfallible(function (opts) {
}, "devtools/client/memory/telemetry#countDiff");
/**
* @param {Object} breakdown
* The breakdown used to label nodes in the dominator tree.
* @param {Object} display
* The display used to label nodes in the dominator tree.
*/
exports.countDominatorTree = makeInfallible(function ({ breakdown }) {
exports.countDominatorTree = makeInfallible(function ({ display }) {
let histogram = telemetry.getHistogramById("DEVTOOLS_MEMORY_DOMINATOR_TREE_COUNT");
histogram.add(1);
histogram = telemetry.getKeyedHistogramById("DEVTOOLS_MEMORY_BREAKDOWN_DOMINATOR_TREE_COUNT");
if (breakdown === dominatorTreeBreakdowns.coarseType.breakdown) {
if (display === dominatorTreeDisplays.coarseType) {
histogram.add(COARSE_TYPE);
} else if (breakdown === dominatorTreeBreakdowns.allocationStack.breakdown) {
} else if (display === dominatorTreeDisplays.allocationStack) {
histogram.add(ALLOCATION_STACK);
} else {
histogram.add(CUSTOM);

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

@ -6,9 +6,9 @@ support-files =
doc_big_tree.html
doc_steady_allocation.html
[browser_memory_allocationStackBreakdown_01.js]
[browser_memory_allocationStackDisplay_01.js]
skip-if = debug # bug 1219554
[browser_memory_breakdowns_01.js]
[browser_memory_displays_01.js]
[browser_memory_clear_snapshots.js]
[browser_memory_diff_01.js]
[browser_memory_dominator_trees_01.js]

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

@ -1,16 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
// Sanity test that we can show allocation stack displays in the tree.
"use strict";
const { waitForTime } = require("devtools/shared/DevToolsUtils");
const { breakdowns } = require("devtools/client/memory/constants");
const { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
const censusDisplayActions = require("devtools/client/memory/actions/census-display");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -20,13 +18,8 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
yield dispatch(toggleInvertedAndRefresh(heapWorker));
ok(getState().inverted, true);
ok(doc.getElementById("invert-tree-checkbox").checked,
"invert-tree-checkbox should be checked");
dispatch(breakdownActions.setBreakdown(breakdowns.allocationStack.breakdown));
is(getState().breakdown.by, "allocationStack");
dispatch(censusDisplayActions.setCensusDisplay(censusDisplays.invertedAllocationStack));
is(getState().censusDisplay.breakdown.by, "allocationStack");
yield dispatch(toggleRecordingAllocationStacks(front));
ok(getState().allocations.recording);

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

@ -1,15 +1,19 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that the heap tree renders rows based on the breakdown
* Tests that the heap tree renders rows based on the display
*/
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 $$ = document.querySelectorAll.bind(document);
function $$(selector) {
return [...document.querySelectorAll(selector)];
}
yield takeSnapshot(panel.panelWin);
@ -17,14 +21,15 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
state.snapshots[0].state === states.SAVED_CENSUS);
info("Check coarse type heap view");
["objects", "other", "scripts", "strings"].forEach(findNameCell);
["Function", "js::Shape", "Object", "strings"].forEach(findNameCell);
yield setBreakdown(panel.panelWin, "allocationStack");
yield setCensusDisplay(panel.panelWin, censusDisplays.allocationStack);
info("Check allocation stack heap view");
[L10N.getStr("tree-item.nostack")].forEach(findNameCell);
function findNameCell (name) {
let el = Array.prototype.find.call($$(".tree .heap-tree-item-name span"), el => el.textContent === name);
function findNameCell(name) {
const el = $$(".tree .heap-tree-item-name span")
.find(e => e.textContent === name);
ok(el, `Found heap tree item cell for ${name}.`);
}
});

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

@ -8,14 +8,11 @@
const {
dominatorTreeState,
snapshotState,
viewState,
} = require("devtools/client/memory/constants");
const {
expandDominatorTreeNode,
takeSnapshotAndCensus,
} = require("devtools/client/memory/actions/snapshot");
const { toggleInverted } = require("devtools/client/memory/actions/inverted");
const { changeView } = require("devtools/client/memory/actions/view");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_big_tree.html";
@ -24,8 +21,6 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
// Taking snapshots and computing dominator trees is slow :-/
requestLongerTimeout(4);
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const store = panel.panelWin.gStore;
const { getState, dispatch } = store;
const doc = panel.panelWin.document;

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

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
// Sanity test that we can show allocation stack displays in the tree.
"use strict";
@ -21,11 +21,6 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = store;
const doc = panel.panelWin.document;
ok(!getState().inverted, "not inverted by default");
const invertCheckbox = doc.getElementById("invert-tree-checkbox");
EventUtils.synthesizeMouseAtCenter(invertCheckbox, {}, panel.panelWin);
yield waitUntilState(store, state => state.inverted === true);
const takeSnapshotButton = doc.getElementById("take-snapshot");
EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);

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

@ -39,7 +39,7 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = store;
const doc = panel.panelWin.document;
is(getState().breakdown.by, "coarseType");
is(getState().censusDisplay.breakdown.by, "coarseType");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
let census = getState().snapshots[0].census;
let root1 = census.report.children[0];

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

@ -1,13 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
// Sanity test that we can show allocation stack displays in the tree.
"use strict";
const { breakdowns } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const censusDisplayActions = require("devtools/client/memory/actions/census-display");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -21,10 +20,11 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
"Should not be recording allocagtions");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
yield dispatch(breakdownActions.setBreakdownAndRefresh(heapWorker,
breakdowns.allocationStack.breakdown));
yield dispatch(censusDisplayActions.setCensusDisplayAndRefresh(
heapWorker,
censusDisplays.allocationStack));
is(getState().breakdown.by, "allocationStack",
is(getState().censusDisplay.breakdown.by, "allocationStack",
"Should be using allocation stack breakdown");
ok(!getState().allocations.recording,

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

@ -16,7 +16,6 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
is(getState().breakdown.by, "coarseType");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
is(getState().allocations.recording, false);
@ -25,23 +24,10 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
is(getState().allocations.recording, true);
const nameElems = [...doc.querySelectorAll(".heap-tree-item-field.heap-tree-item-name")];
is(nameElems.length, 4, "Should get 4 items, one for each coarse type");
for (let el of nameElems) {
dumpn(`Found ${el.textContent.trim()}`);
}
ok(nameElems.some(e => e.textContent.indexOf("objects") >= 0),
"One for coarse type 'objects'");
ok(nameElems.some(e => e.textContent.indexOf("scripts") >= 0),
"One for coarse type 'scripts'");
ok(nameElems.some(e => e.textContent.indexOf("strings") >= 0),
"One for coarse type 'strings'");
ok(nameElems.some(e => e.textContent.indexOf("other") >= 0),
"One for coarse type 'other'");
for (let e of nameElems) {
is(e.style.marginLeft, "0px",
is(el.style.marginLeft, "0px",
"None of the elements should be an indented/expanded child");
}
});

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

@ -5,9 +5,7 @@
"use strict";
const { breakdowns } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -28,7 +26,7 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const doc = panel.panelWin.document;
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
is(getState().breakdown.by, "coarseType",
is(getState().censusDisplay.breakdown.by, "coarseType",
"Should be using coarse type breakdown");
const bytesCells = [...doc.querySelectorAll(".heap-tree-item-bytes")];

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

@ -13,8 +13,8 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
this);
var { snapshotState: states } = require("devtools/client/memory/constants");
var { breakdownEquals, breakdownNameToSpec, L10N } = require("devtools/client/memory/utils");
var { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
var { L10N } = require("devtools/client/memory/utils");
Services.prefs.setBoolPref("devtools.memory.enabled", true);
@ -117,22 +117,22 @@ function clearSnapshots (window) {
}
/**
* Sets breakdown and waits for currently selected breakdown to use it
* and be completed the census.
* Sets the current requested display and waits for the selected snapshot to use
* it and complete the new census that entails.
*/
function setBreakdown (window, type) {
info(`Setting breakdown to ${type}...`);
function setCensusDisplay(window, display) {
info(`Setting census display to ${display}...`);
let { gStore, gHeapAnalysesClient } = window;
// XXX: Should handle this via clicking the DOM, but React doesn't
// fire the onChange event, so just change it in the store.
// window.document.querySelector(`.select-breakdown`).value = type;
gStore.dispatch(require("devtools/client/memory/actions/breakdown")
.setBreakdownAndRefresh(gHeapAnalysesClient, breakdownNameToSpec(type)));
// window.document.querySelector(`.select-display`).value = type;
gStore.dispatch(require("devtools/client/memory/actions/census-display")
.setCensusDisplayAndRefresh(gHeapAnalysesClient, display));
return waitUntilState(window.gStore, () => {
let selected = window.gStore.getState().snapshots.find(s => s.selected);
return selected.state === states.SAVED_CENSUS &&
breakdownEquals(breakdownNameToSpec(type), selected.census.breakdown);
selected.census.display === display;
});
}

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

@ -14,4 +14,3 @@ support-files =
[test_ShortestPaths_01.html]
[test_ShortestPaths_02.html]
[test_Toolbar_01.html]
[test_Toolbar_02.html]

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

@ -17,17 +17,15 @@ var { immutableUpdate } = DevToolsUtils;
var constants = require("devtools/client/memory/constants");
var {
breakdowns,
censusDisplays,
diffingState,
dominatorTreeBreakdowns,
dominatorTreeDisplays,
dominatorTreeState,
snapshotState,
viewState
} = constants;
const {
getBreakdownDisplayData,
getDominatorTreeBreakdownDisplayData,
L10N,
} = require("devtools/client/memory/utils");
@ -122,7 +120,7 @@ var TEST_DOMINATOR_TREE = Object.freeze({
expanded: new Set(),
focused: null,
error: null,
breakdown: dominatorTreeBreakdowns.coarseType.breakdown,
display: dominatorTreeDisplays.coarseType,
activeFetchRequestCount: null,
state: dominatorTreeState.LOADED,
});
@ -173,12 +171,17 @@ var TEST_HEAP_PROPS = Object.freeze({
strings: Object.freeze({ count: 2, bytes: 200 }),
other: Object.freeze({ count: 1, bytes: 100 }),
}),
breakdown: Object.freeze({
by: "coarseType",
objects: Object.freeze({ by: "count", count: true, bytes: true }),
scripts: Object.freeze({ by: "count", count: true, bytes: true }),
strings: Object.freeze({ by: "count", count: true, bytes: true }),
other: Object.freeze({ by: "count", count: true, bytes: true }),
display: Object.freeze({
displayName: "Test Display",
tooltip: "Test display tooltup",
inverted: false,
breakdown: Object.freeze({
by: "coarseType",
objects: Object.freeze({ by: "count", count: true, bytes: true }),
scripts: Object.freeze({ by: "count", count: true, bytes: true }),
strings: Object.freeze({ by: "count", count: true, bytes: true }),
other: Object.freeze({ by: "count", count: true, bytes: true }),
}),
}),
inverted: false,
filter: null,
@ -196,10 +199,14 @@ var TEST_HEAP_PROPS = Object.freeze({
});
var TEST_TOOLBAR_PROPS = Object.freeze({
breakdowns: getBreakdownDisplayData(),
censusDisplays: [
censusDisplays.coarseType,
censusDisplays.allocationStack,
censusDisplays.invertedAllocationStack,
],
onTakeSnapshotClick: noop,
onImportClick: noop,
onBreakdownChange: noop,
onCensusDisplayChange: noop,
onToggleRecordAllocationStacks: noop,
allocations: models.allocations,
onToggleInverted: noop,
@ -210,8 +217,11 @@ var TEST_TOOLBAR_PROPS = Object.freeze({
onToggleDiffing: noop,
view: viewState.CENSUS,
onViewChange: noop,
dominatorTreeBreakdowns: getDominatorTreeBreakdownDisplayData(),
onDominatorTreeBreakdownChange: noop,
dominatorTreeDisplays: [
dominatorTreeDisplays.coarseType,
dominatorTreeDisplays.allocationStack,
],
onDominatorTreeDisplayChange: noop,
snapshots: [],
});

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

@ -41,7 +41,7 @@ Test that we show the "hey you're not recording allocation stacks" message at th
}
]
},
breakdown: breakdowns.allocationStack.breakdown,
display: censusDisplays.allocationStack,
}),
}),
})), container);
@ -82,7 +82,7 @@ Test that we show the "hey you're not recording allocation stacks" message at th
}
]
},
breakdown: breakdowns.allocationStack.breakdown,
display: censusDisplays.allocationStack,
}),
}),
})), container);
@ -102,7 +102,7 @@ Test that we show the "hey you're not recording allocation stacks" message at th
parent: undefined,
children: undefined
},
breakdown: breakdowns.allocationStack.breakdown,
display: censusDisplays.allocationStack,
}),
}),
})), container);

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

@ -1,47 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test that the Toolbar component shows the tree inversion checkbox only at the appropriate times.
-->
<head>
<meta charset="utf-8">
<title>Tree component test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<div id="container"></div>
<pre id="test">
<script src="head.js" type="application/javascript;version=1.8"></script>
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
const container = document.getElementById("container");
// Census and dominator tree views.
for (let view of [viewState.CENSUS, viewState.DIFFING]) {
yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
view,
})), container);
ok(container.querySelector("#invert-tree-checkbox"),
`The invert checkbox is shown in view = ${view}`);
}
yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
view: viewState.DOMINATOR_TREE,
})), container);
ok(!container.querySelector("#invert-tree-checkbox"),
"The invert checkbox is NOT shown in the DOMINATOR_TREE view");
} catch(e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
SimpleTest.finish();
}
});
</script>
</pre>
</body>
</html>

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

@ -73,21 +73,6 @@ function waitUntilSnapshotState (store, expected) {
return waitUntilState(store, predicate);
}
function isBreakdownType (report, type) {
// Little sanity check, all reports should have at least a children array.
if (!report || !Array.isArray(report.children)) {
return false;
}
switch (type) {
case "coarseType":
return report.children.find(c => c.name === "objects");
case "allocationStack":
return report.children.find(c => c.name === "noStack");
default:
throw new Error(`isBreakdownType does not yet support ${type}`);
}
}
function *createTempFile () {
let file = FileUtils.getFile("TmpD", ["tmp.fxsnapshot"]);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);

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

@ -1,9 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that changing filter state properly refreshes the selected census.
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { snapshotState: states } = require("devtools/client/memory/constants");
let { setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
@ -11,7 +12,7 @@ function run_test() {
run_next_test();
}
add_task(function *() {
add_task(function*() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();

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

@ -1,5 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `importSnapshotAndCensus()` for the whole flow of
@ -7,7 +8,6 @@
*/
let { actions, snapshotState: states } = require("devtools/client/memory/constants");
let { breakdownEquals } = require("devtools/client/memory/utils");
let { exportSnapshot, importSnapshotAndCensus } = require("devtools/client/memory/actions/io");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
@ -61,8 +61,8 @@ add_task(function *() {
let snapshot1 = getState().snapshots[0];
let snapshot2 = getState().snapshots[1];
ok(breakdownEquals(snapshot1.breakdown, snapshot2.breakdown),
"imported snapshot has correct breakdown");
equal(snapshot1.display, snapshot2.display,
"imported snapshot has correct display");
// Clone the census data so we can destructively remove the ID/parents to compare
// equal census data

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

@ -1,98 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the task creator `setBreakdownAndRefreshAndRefresh()` for breakdown changing.
* We test this rather than `setBreakdownAndRefresh` directly, as we use the refresh action
* in the app itself composed from `setBreakdownAndRefresh`
*/
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { breakdownEquals } = require("devtools/client/memory/utils");
let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default breakdown with no snapshots
equal(getState().breakdown.by, "coarseType", "default coarseType breakdown selected at start.");
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown));
equal(getState().breakdown.by, "allocationStack", "breakdown changed with no snapshots");
// Test invalid breakdowns
ok(getState().errors.length === 0, "No error actions in the queue.");
dispatch(setBreakdownAndRefresh(heapWorker, {}));
yield waitUntilState(store, () => getState().errors.length === 1);
ok(true, "Emits an error action when passing in an invalid breakdown object");
equal(getState().breakdown.by, "allocationStack",
"current breakdown unchanged when passing invalid breakdown");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(isBreakdownType(getState().snapshots[0].census.report, "allocationStack"),
"New snapshots use the current, non-default breakdown");
// Updates when changing breakdown during `SAVING`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING]);
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.coarseType.breakdown));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(isBreakdownType(getState().snapshots[1].census.report, "coarseType"),
"Breakdown can be changed while saving snapshots, uses updated breakdown in census");
// Updates when changing breakdown during `SAVING_CENSUS`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]);
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(breakdownEquals(getState().snapshots[2].census.breakdown, breakdowns.allocationStack.breakdown),
"Breakdown can be changed while saving census, stores updated breakdown in snapshot");
ok(isBreakdownType(getState().snapshots[2].census.report, "allocationStack"),
"Breakdown can be changed while saving census, uses updated breakdown in census");
// Updates census on currently selected snapshot when changing breakdown
ok(getState().snapshots[2].selected, "Third snapshot currently selected");
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.coarseType.breakdown));
yield waitUntilState(store, () => isBreakdownType(getState().snapshots[2].census.report, "coarseType"));
ok(isBreakdownType(getState().snapshots[2].census.report, "coarseType"),
"Snapshot census updated when changing breakdowns after already generating one census");
dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown));
yield waitUntilState(store, () => isBreakdownType(getState().snapshots[2].census.report, "allocationStack"));
// Does not update unselected censuses
ok(!getState().snapshots[1].selected, "Second snapshot unselected currently");
ok(breakdownEquals(getState().snapshots[1].census.breakdown, breakdowns.coarseType.breakdown),
"Second snapshot using `coarseType` breakdown still and not yet updated to correct breakdown");
ok(isBreakdownType(getState().snapshots[1].census.report, "coarseType"),
"Second snapshot using `coarseType` still for census and not yet updated to correct breakdown");
// Updates to current breakdown when switching to stale snapshot
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING_CENSUS, states.SAVED_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(getState().snapshots[1].selected, "Second snapshot selected currently");
ok(breakdownEquals(getState().snapshots[1].census.breakdown, breakdowns.allocationStack.breakdown),
"Second snapshot using `allocationStack` breakdown and updated to correct breakdown");
ok(isBreakdownType(getState().snapshots[1].census.report, "allocationStack"),
"Second snapshot using `allocationStack` for census and updated to correct breakdown");
heapWorker.destroy();
yield front.detach();
});

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

@ -1,45 +0,0 @@
/* 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 { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { setBreakdown } = require("devtools/client/memory/actions/breakdown");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default breakdown with no snapshots
equal(getState().breakdown.by, "coarseType", "default coarseType breakdown selected at start.");
dispatch(setBreakdown(breakdowns.allocationStack.breakdown));
equal(getState().breakdown.by, "allocationStack", "breakdown changed with no snapshots");
// Test invalid breakdowns
try {
dispatch(setBreakdown({}));
ok(false, "Throws when passing in an invalid breakdown object");
} catch (e) {
ok(true, "Throws when passing in an invalid breakdown object");
}
equal(getState().breakdown.by, "allocationStack",
"current breakdown unchanged when passing invalid breakdown");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(isBreakdownType(getState().snapshots[0].census.report, "allocationStack"),
"New snapshots use the current, non-default breakdown");
});

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

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for display
* changing. We test this rather than `setCensusDisplayAndRefresh` directly, as
* we use the refresh action in the app itself composed from
* `setCensusDisplayAndRefresh`.
*/
let { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default display with no snapshots
equal(getState().censusDisplay.breakdown.by, "coarseType",
"default coarseType display selected at start.");
dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.allocationStack));
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"display changed with no snapshots");
// Test invalid displays
ok(getState().errors.length === 0, "No error actions in the queue.");
dispatch(setCensusDisplayAndRefresh(heapWorker, {}));
yield waitUntilState(store, () => getState().errors.length === 1);
ok(true, "Emits an error action when passing in an invalid display object");
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"current display unchanged when passing invalid display");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
// Updates when changing display during `SAVING`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING]);
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]);
// Updates when changing display during `SAVING_CENSUS`
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]);
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
"Display can be changed while saving census, stores updated display in snapshot");
// Updates census on currently selected snapshot when changing display
ok(getState().snapshots[2].selected, "Third snapshot currently selected");
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
yield waitUntilState(store, state => state.snapshots[2].state === states.SAVED_CENSUS);
equal(getState().snapshots[2].census.display, censusDisplays.coarseType,
"Snapshot census updated when changing displays after already generating one census");
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
yield waitUntilState(store, state => state.snapshots[2].state === states.SAVED_CENSUS);
equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
"Snapshot census updated when changing displays after already generating one census");
// Does not update unselected censuses
ok(!getState().snapshots[1].selected, "Second snapshot unselected currently");
equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
"Second snapshot using `coarseType` display still and not yet updated to correct display");
// Updates to current display when switching to stale snapshot
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING_CENSUS, states.SAVED_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]);
ok(getState().snapshots[1].selected, "Second snapshot selected currently");
equal(getState().snapshots[1].census.display, censusDisplays.allocationStack,
"Second snapshot using `allocationStack` display and updated to correct display");
heapWorker.destroy();
yield front.detach();
});

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

@ -1,16 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `setBreakdownAndRefreshAndRefresh()` for custom
* breakdowns.
* Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for custom
* displays.
*/
let { snapshotState: states } = require("devtools/client/memory/constants");
let { breakdownEquals } = require("devtools/client/memory/utils");
let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown");
let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
let custom = { by: "internalType", then: { by: "count", bytes: true, count: false }};
let CUSTOM = {
displayName: "Custom",
tooltip: "Custom tooltip",
inverted: false,
breakdown: {
by: "internalType",
then: { by: "count", bytes: true, count: false }
}
};
function run_test() {
run_next_test();
@ -23,20 +32,20 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
dispatch(setBreakdownAndRefresh(heapWorker, custom));
ok(breakdownEquals(getState().breakdown, custom),
"Custom breakdown stored in breakdown state.");
dispatch(setCensusDisplayAndRefresh(heapWorker, CUSTOM));
equal(getState().censusDisplay, CUSTOM,
"CUSTOM display stored in display state.");
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(breakdownEquals(getState().snapshots[0].census.breakdown, custom),
"New snapshot stored custom breakdown when done taking census");
equal(getState().snapshots[0].census.display, CUSTOM,
"New snapshot stored CUSTOM display when done taking census");
ok(getState().snapshots[0].census.report.children.length, "Census has some children");
// Ensure we don't have `count` in any results
ok(getState().snapshots[0].census.report.children.every(c => !c.count),
"Census used custom breakdown without counts");
"Census used CUSTOM display without counts");
// Ensure we do have `bytes` in the results
ok(getState().snapshots[0].census.report.children.every(c => typeof c.bytes === "number"),
"Census used custom breakdown with bytes");
"Census used CUSTOM display with bytes");
});

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

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the action creator `setCensusDisplay()` for display changing. Does not
* test refreshing the census information, check `setCensusDisplayAndRefresh`
* action for that.
*/
let { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
let { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function*() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
// Test default display with no snapshots
equal(getState().censusDisplay.breakdown.by, "coarseType",
"default coarseType display selected at start.");
dispatch(setCensusDisplay(censusDisplays.allocationStack));
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"display changed with no snapshots");
// Test invalid displays
try {
dispatch(setCensusDisplay({}));
ok(false, "Throws when passing in an invalid display object");
} catch (e) {
ok(true, "Throws when passing in an invalid display object");
}
equal(getState().censusDisplay.breakdown.by, "allocationStack",
"current display unchanged when passing invalid display");
// Test new snapshots
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
"New snapshots use the current, non-default display");
});

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

@ -5,9 +5,7 @@
* Tests the async reducer responding to the action `takeCensus(heapWorker, snapshot)`
*/
var { snapshotState: states, breakdowns } = require("devtools/client/memory/constants");
var { breakdownEquals } = require("devtools/client/memory/utils");
var { ERROR_TYPE } = require("devtools/client/shared/redux/middleware/task");
var { snapshotState: states, censusDisplays } = require("devtools/client/memory/constants");
var actions = require("devtools/client/memory/actions/snapshot");
function run_test() {
@ -45,8 +43,6 @@ add_task(function *() {
snapshot = store.getState().snapshots[0];
ok(snapshot.census, "Snapshot has census after saved census");
ok(snapshot.census.report.children.length, "Census is in tree node form");
ok(isBreakdownType(snapshot.census.report, "coarseType"),
"Census is in tree node form with the default breakdown");
ok(breakdownEquals(snapshot.census.breakdown, breakdowns.coarseType.breakdown),
"Snapshot stored correct breakdown used for the census");
equal(snapshot.census.display, censusDisplays.coarseType,
"Snapshot stored correct display used for the census");
});

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

@ -1,11 +1,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that changing inverted state properly refreshes the selected census.
// Test that changing displays with different inverted state properly
// refreshes the selected census.
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
const {
censusDisplays,
snapshotState: states,
} = require("devtools/client/memory/constants");
const {
setCensusDisplayAndRefresh
} = require("devtools/client/memory/actions/census-display");
const {
takeSnapshotAndCensus,
selectSnapshotAndRefresh,
} = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
@ -18,7 +27,9 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
equal(getState().inverted, false, "not inverted by default");
// Select a non-inverted display.
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false, "not inverted by default");
dispatch(takeSnapshotAndCensus(front, heapWorker));
dispatch(takeSnapshotAndCensus(front, heapWorker));
@ -29,21 +40,23 @@ add_task(function *() {
states.SAVED_CENSUS]);
ok(true, "saved 3 snapshots and took a census of each of them");
dispatch(toggleInvertedAndRefresh(heapWorker));
// Select an inverted display.
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
states.SAVED_CENSUS,
states.SAVING_CENSUS]);
ok(true, "toggling inverted should recompute the selected snapshot's census");
equal(getState().inverted, true, "now inverted");
equal(getState().censusDisplay.inverted, true, "now inverted");
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
states.SAVED_CENSUS,
states.SAVED_CENSUS]);
equal(getState().snapshots[0].census.inverted, false);
equal(getState().snapshots[1].census.inverted, false);
equal(getState().snapshots[2].census.inverted, true);
equal(getState().snapshots[0].census.display.inverted, false);
equal(getState().snapshots[1].census.display.inverted, false);
equal(getState().snapshots[2].census.display.inverted, true);
dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
@ -55,9 +68,9 @@ add_task(function *() {
states.SAVED_CENSUS,
states.SAVED_CENSUS]);
equal(getState().snapshots[0].census.inverted, false);
equal(getState().snapshots[1].census.inverted, true);
equal(getState().snapshots[2].census.inverted, true);
equal(getState().snapshots[0].census.display.inverted, false);
equal(getState().snapshots[1].census.display.inverted, true);
equal(getState().snapshots[2].census.display.inverted, true);
heapWorker.destroy();
yield front.detach();

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

@ -1,12 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that changing inverted state in the middle of taking a snapshot results
// in an inverted census.
let { snapshotState: states } = require("devtools/client/memory/constants");
let { toggleInverted, toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
function run_test() {
run_next_test();
@ -19,26 +20,29 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false);
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVING]);
dispatch(toggleInverted());
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().inverted,
ok(getState().censusDisplay.inverted,
"should want inverted trees");
ok(getState().snapshots[0].census.inverted,
ok(getState().snapshots[0].census.display.inverted,
"snapshot-we-were-in-the-middle-of-saving's census should be inverted");
dispatch(toggleInvertedAndRefresh(heapWorker));
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]);
ok(true, "toggling inverted retriggers census");
ok(!getState().inverted, "no long inverted");
ok(!getState().censusDisplay.inverted, "no longer inverted");
dispatch(toggleInverted());
dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().inverted, "inverted again");
ok(getState().snapshots[0].census.inverted,
ok(getState().censusDisplay.inverted, "inverted again");
ok(getState().snapshots[0].census.display.inverted,
"census-we-were-in-the-middle-of-recomputing should be inverted again");
heapWorker.destroy();

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

@ -1,29 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test toggling the top level inversion state of the tree.
let { toggleInverted } = require("devtools/client/memory/actions/inverted");
const { censusDisplays } = require("devtools/client/memory/constants");
const { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
add_task(function*() {
let store = Store();
const { getState, dispatch } = store;
equal(getState().inverted, false, "not inverted by default");
dispatch(setCensusDisplay(censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false,
"not inverted initially");
dispatch(toggleInverted());
equal(getState().inverted, true, "now inverted after toggling");
dispatch(setCensusDisplay(censusDisplays.invertedAllocationStack));
equal(getState().censusDisplay.inverted, true, "now inverted after toggling");
dispatch(toggleInverted());
equal(getState().inverted, false, "not inverted again after toggling again");
heapWorker.destroy();
yield front.detach();
dispatch(setCensusDisplay(censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false,
"not inverted again after toggling again");
});

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

@ -1,10 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the action creator `setBreakdown()` for breakdown changing.
* Does not test refreshing the census information, check `setBreakdownAndRefresh` action
* for that.
* Test toggling the recording of allocation stacks.
*/
let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
@ -15,7 +14,6 @@ function run_test() {
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
@ -39,4 +37,6 @@ add_task(function *() {
equal(getState().allocations.recording, false, "now we are not recording");
ok(front.recordingAllocations, "front is not recording anymore");
yield front.detach();
});

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

@ -15,7 +15,6 @@ const {
takeSnapshot,
readSnapshot
} = require("devtools/client/memory/actions/snapshot");
const { breakdownEquals } = require("devtools/client/memory/utils");
function run_test() {
run_next_test();
@ -63,11 +62,12 @@ add_task(function *() {
ok(true, "And then the diff should complete.");
ok(getState().diffing.census, "And we should have a census.");
ok(getState().diffing.census.report, "And that census should have a report.");
ok(breakdownEquals(getState().diffing.census.breakdown, getState().breakdown),
"And that census should have the correct breakdown");
equal(getState().diffing.census.display, getState().censusDisplay,
"And that census should have the correct display");
equal(getState().diffing.census.filter, getState().filter,
"And that census should have the correct filter");
equal(getState().diffing.census.inverted, getState().inverted,
equal(getState().diffing.census.display.inverted,
getState().censusDisplay.inverted,
"And that census should have the correct inversion");
heapWorker.destroy();

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

@ -6,11 +6,11 @@
const {
diffingState,
snapshotState,
breakdowns,
censusDisplays,
} = require("devtools/client/memory/constants");
const {
setBreakdownAndRefresh,
} = require("devtools/client/memory/actions/breakdown");
setCensusDisplayAndRefresh,
} = require("devtools/client/memory/actions/census-display");
const {
toggleDiffing,
selectSnapshotForDiffingAndRefresh,
@ -18,14 +18,10 @@ const {
const {
setFilterStringAndRefresh,
} = require("devtools/client/memory/actions/filter");
const {
toggleInvertedAndRefresh,
} = require("devtools/client/memory/actions/inverted");
const {
takeSnapshot,
readSnapshot,
} = require("devtools/client/memory/actions/snapshot");
const { breakdownEquals } = require("devtools/client/memory/utils");
function run_test() {
run_next_test();
@ -38,6 +34,11 @@ add_task(function *() {
let store = Store();
const { getState, dispatch } = store;
yield dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.allocationStack));
equal(getState().censusDisplay.inverted, false,
"not inverted at start");
equal(getState().diffing, null, "not diffing by default");
const s1 = yield dispatch(takeSnapshot(front, heapWorker));
@ -61,17 +62,19 @@ add_task(function *() {
const shouldTriggerRecompute = [
{
name: "toggling inversion",
func: () => dispatch(toggleInvertedAndRefresh(heapWorker))
func: () => dispatch(setCensusDisplayAndRefresh(
heapWorker,
censusDisplays.invertedAllocationStack))
},
{
name: "filtering",
func: () => dispatch(setFilterStringAndRefresh("scr", heapWorker))
},
{
name: "changing breakdowns",
name: "changing displays",
func: () =>
dispatch(setBreakdownAndRefresh(heapWorker,
breakdowns.allocationStack.breakdown))
dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.coarseType))
}
];
@ -91,12 +94,13 @@ add_task(function *() {
ok(getState().diffing.census, "And we should have a census.");
ok(getState().diffing.census.report,
"And that census should have a report.");
ok(breakdownEquals(getState().diffing.census.breakdown,
getState().breakdown),
"And that census should have the correct breakdown");
equal(getState().diffing.census.display,
getState().censusDisplay,
"And that census should have the correct display");
equal(getState().diffing.census.filter, getState().filter,
"And that census should have the correct filter");
equal(getState().diffing.census.inverted, getState().inverted,
equal(getState().diffing.census.display.inverted,
getState().censusDisplay.inverted,
"And that census should have the correct inversion");
}

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

@ -1,18 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we can change the breakdown with which we describe a dominator tree
// Test that we can change the display with which we describe a dominator tree
// and that the dominator tree is re-fetched.
const {
snapshotState: states,
dominatorTreeState,
viewState,
dominatorTreeBreakdowns,
dominatorTreeDisplays,
} = require("devtools/client/memory/constants");
const {
setDominatorTreeBreakdownAndRefresh
} = require("devtools/client/memory/actions/dominatorTreeBreakdown");
setDominatorTreeDisplayAndRefresh
} = require("devtools/client/memory/actions/dominator-tree-display");
const {
changeView,
} = require("devtools/client/memory/actions/view");
@ -46,34 +46,34 @@ add_task(function *() {
state.snapshots[0].dominatorTree &&
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
ok(getState().dominatorTreeBreakdown,
"We have a default breakdown for describing nodes in a dominator tree");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.coarseType.breakdown,
ok(getState().dominatorTreeDisplay,
"We have a default display for describing nodes in a dominator tree");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.coarseType,
"and the default is coarse type");
equal(getState().dominatorTreeBreakdown,
getState().snapshots[0].dominatorTree.breakdown,
"and the newly computed dominator tree has that breakdown");
equal(getState().dominatorTreeDisplay,
getState().snapshots[0].dominatorTree.display,
"and the newly computed dominator tree has that display");
// Switch to the allocationStack breakdown.
dispatch(setDominatorTreeBreakdownAndRefresh(
// Switch to the allocationStack display.
dispatch(setDominatorTreeDisplayAndRefresh(
heapWorker,
dominatorTreeBreakdowns.allocationStack.breakdown));
dominatorTreeDisplays.allocationStack));
yield waitUntilState(store, state =>
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
ok(true,
"switching breakdown types caused the dominator tree to be fetched " +
"switching display types caused the dominator tree to be fetched " +
"again.");
yield waitUntilState(store, state =>
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
equal(getState().snapshots[0].dominatorTree.breakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"The new dominator tree's breakdown is allocationStack");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"as is our requested dominator tree breakdown");
equal(getState().snapshots[0].dominatorTree.display,
dominatorTreeDisplays.allocationStack,
"The new dominator tree's display is allocationStack");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.allocationStack,
"as is our requested dominator tree display");
heapWorker.destroy();
yield front.detach();

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

@ -1,18 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we can change the breakdown with which we describe a dominator tree
// Test that we can change the display with which we describe a dominator tree
// while the dominator tree is in the middle of being fetched.
const {
snapshotState: states,
dominatorTreeState,
viewState,
dominatorTreeBreakdowns,
dominatorTreeDisplays,
} = require("devtools/client/memory/constants");
const {
setDominatorTreeBreakdownAndRefresh
} = require("devtools/client/memory/actions/dominatorTreeBreakdown");
setDominatorTreeDisplayAndRefresh
} = require("devtools/client/memory/actions/dominator-tree-display");
const {
changeView,
} = require("devtools/client/memory/actions/view");
@ -46,31 +46,31 @@ add_task(function *() {
state.snapshots[0].dominatorTree &&
state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
ok(getState().dominatorTreeBreakdown,
"We have a default breakdown for describing nodes in a dominator tree");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.coarseType.breakdown,
ok(getState().dominatorTreeDisplay,
"We have a default display for describing nodes in a dominator tree");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.coarseType,
"and the default is coarse type");
equal(getState().dominatorTreeBreakdown,
getState().snapshots[0].dominatorTree.breakdown,
"and the newly computed dominator tree has that breakdown");
equal(getState().dominatorTreeDisplay,
getState().snapshots[0].dominatorTree.display,
"and the newly computed dominator tree has that display");
// Switch to the allocationStack breakdown while we are still fetching the
// Switch to the allocationStack display while we are still fetching the
// dominator tree.
dispatch(setDominatorTreeBreakdownAndRefresh(
dispatch(setDominatorTreeDisplayAndRefresh(
heapWorker,
dominatorTreeBreakdowns.allocationStack.breakdown));
dominatorTreeDisplays.allocationStack));
// Wait for the dominator tree to finish being fetched.
yield waitUntilState(store, state =>
state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
equal(getState().snapshots[0].dominatorTree.breakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"The new dominator tree's breakdown is allocationStack");
equal(getState().dominatorTreeBreakdown,
dominatorTreeBreakdowns.allocationStack.breakdown,
"as is our requested dominator tree breakdown");
equal(getState().snapshots[0].dominatorTree.display,
dominatorTreeDisplays.allocationStack,
"The new dominator tree's display is allocationStack");
equal(getState().dominatorTreeDisplay,
dominatorTreeDisplays.allocationStack,
"as is our requested dominator tree display");
heapWorker.destroy();
yield front.detach();

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

@ -1,15 +1,16 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that we use the correct snapshot aggregate value
* in `utils.getSnapshotTotals(snapshot)`
*/
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { getSnapshotTotals, breakdownEquals } = require("devtools/client/memory/utils");
let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
const { getSnapshotTotals } = require("devtools/client/memory/utils");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
function run_test() {
run_next_test();
@ -22,12 +23,13 @@ add_task(function *() {
let store = Store();
let { getState, dispatch } = store;
yield dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.allocationStack));
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(!getState().snapshots[0].census.inverted, "Snapshot is not inverted");
ok(isBreakdownType(getState().snapshots[0].census.report, "coarseType"),
"Snapshot using `coarseType` breakdown");
ok(!getState().snapshots[0].census.display.inverted, "Snapshot is not inverted");
let census = getState().snapshots[0].census;
let result = aggregate(census.report);
@ -41,14 +43,17 @@ add_task(function *() {
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes");
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count");
dispatch(toggleInvertedAndRefresh(heapWorker));
dispatch(setCensusDisplayAndRefresh(heapWorker,
censusDisplays.invertedAllocationStack));
yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().snapshots[0].census.inverted, "Snapshot is inverted");
ok(getState().snapshots[0].census.display.inverted, "Snapshot is inverted");
result = getSnapshotTotals(getState().snapshots[0].census);
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes when inverted");
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count when inverted");
equal(totalBytes, result.bytes,
"getSnapshotTotals reuslted in correct bytes when inverted");
equal(totalCount, result.count,
"getSnapshotTotals reuslted in correct count when inverted");
});
function aggregate (report) {

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

@ -1,5 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests the task creator `takeSnapshotAndCensus()` for the whole flow of
@ -8,7 +9,7 @@
*/
let utils = require("devtools/client/memory/utils");
let { snapshotState: states, breakdowns } = require("devtools/client/memory/constants");
let { snapshotState: states, censusDisplays } = require("devtools/client/memory/constants");
let { Preferences } = require("resource://gre/modules/Preferences.jsm");
function run_test() {
@ -16,39 +17,16 @@ function run_test() {
}
add_task(function *() {
ok(utils.breakdownEquals(breakdowns.allocationStack.breakdown, {
by: "allocationStack",
then: { by: "count", count: true, bytes: true },
noStack: { by: "count", count: true, bytes: true },
}), "utils.breakdownEquals() passes with preset"),
ok(!utils.breakdownEquals(breakdowns.allocationStack.breakdown, {
by: "allocationStack",
then: { by: "count", count: false, bytes: true },
noStack: { by: "count", count: true, bytes: true },
}), "utils.breakdownEquals() fails when deep properties do not match");
ok(!utils.breakdownEquals(breakdowns.allocationStack.breakdown, {
by: "allocationStack",
then: { by: "count", bytes: true },
noStack: { by: "count", count: true, bytes: true },
}), "utils.breakdownEquals() fails when deep properties are missing.");
let s1 = utils.createSnapshot({});
let s2 = utils.createSnapshot({});
equal(s1.state, states.SAVING, "utils.createSnapshot() creates snapshot in saving state");
ok(s1.id !== s2.id, "utils.createSnapshot() creates snapshot with unique ids");
ok(utils.breakdownEquals(utils.breakdownNameToSpec("coarseType"), breakdowns.coarseType.breakdown),
"utils.breakdownNameToSpec() works for presets");
ok(utils.breakdownEquals(utils.breakdownNameToSpec("coarseType"), breakdowns.coarseType.breakdown),
"utils.breakdownNameToSpec() works for presets");
let custom = { by: "internalType", then: { by: "count", bytes: true }};
Preferences.set("devtools.memory.custom-breakdowns", JSON.stringify({ "My Breakdown": custom }));
Preferences.set("devtools.memory.custom-census-displays", JSON.stringify({ "My Display": custom }));
ok(utils.breakdownEquals(utils.getCustomBreakdowns()["My Breakdown"], custom),
"utils.getCustomBreakdowns() returns custom breakdowns");
equal(utils.getCustomCensusDisplays()["My Display"].by, custom.by,
"utils.getCustomCensusDisplays() returns custom displays");
ok(true, "test formatNumber util functions");
equal(utils.formatNumber(12), "12", "formatNumber returns 12 for 12");

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

@ -21,9 +21,9 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-filter-03.js]
[test_action-import-snapshot-and-census.js]
[test_action-select-snapshot.js]
[test_action-set-breakdown.js]
[test_action-set-breakdown-and-refresh-01.js]
[test_action-set-breakdown-and-refresh-02.js]
[test_action-set-display.js]
[test_action-set-display-and-refresh-01.js]
[test_action-set-display-and-refresh-02.js]
[test_action-take-census.js]
[test_action-take-snapshot.js]
[test_action-take-snapshot-and-census.js]

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

@ -11,20 +11,20 @@ const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI);
const { OS } = require("resource://gre/modules/osfile.jsm");
const { assert } = require("devtools/shared/DevToolsUtils");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
const CUSTOM_DOMINATOR_TREE_BREAKDOWN_PREF = "devtools.memory.custom-dominator-tree-breakdowns";
const CUSTOM_CENSUS_DISPLAY_PREF = "devtools.memory.custom-census-displays";
const CUSTOM_DOMINATOR_TREE_DISPLAY_PREF = "devtools.memory.custom-dominator-tree-displays";
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {
snapshotState: states,
diffingState,
breakdowns,
dominatorTreeBreakdowns,
censusDisplays,
dominatorTreeDisplays,
dominatorTreeState
} = require("./constants");
/**
* Takes a snapshot object and returns the
* localized form of its timestamp to be used as a title.
* Takes a snapshot object and returns the localized form of its timestamp to be
* used as a title.
*
* @param {Snapshot} snapshot
* @return {String}
@ -48,160 +48,35 @@ exports.getSnapshotTitle = function (snapshot) {
});
};
/**
* Returns an array of objects with the unique key `name`
* and `displayName` for each breakdown.
*
* @return {Object{name, displayName}}
*/
exports.getBreakdownDisplayData = function () {
return exports.getBreakdownNames().map(({ name, tooltip }) => {
// If it's a preset use the display name value
let preset = breakdowns[name];
let displayName = name;
if (preset && preset.displayName) {
displayName = preset.displayName;
}
return { name, tooltip, displayName };
});
};
/**
* Returns an array of the unique names and tooltips for each breakdown in
* presets and custom pref.
*
* @return {Array<Object>}
*/
exports.getBreakdownNames = function () {
let custom = exports.getCustomBreakdowns();
return Object.keys(Object.assign({}, breakdowns, custom))
.map(key => {
return breakdowns[key]
? { name: key, tooltip: breakdowns[key].tooltip }
: { name: key };
});
};
/**
* Returns custom breakdowns defined in `devtools.memory.custom-breakdowns` pref.
*
* @return {Object}
*/
exports.getCustomBreakdowns = function () {
let customBreakdowns = Object.create(null);
function getCustomDisplaysHelper(pref) {
let customDisplays = Object.create(null);
try {
customBreakdowns = JSON.parse(Preferences.get(CUSTOM_BREAKDOWN_PREF)) || Object.create(null);
customDisplays = JSON.parse(Preferences.get(pref)) || Object.create(null);
} catch (e) {
DevToolsUtils.reportException(
`String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`);
`String stored in "${pref}" pref cannot be parsed by \`JSON.parse()\`.`);
}
return customBreakdowns;
return Object.freeze(customDisplays);
}
/**
* Converts a breakdown preset name, like "allocationStack", and returns the
* spec for the breakdown. Also checks properties of keys in the `devtools.memory.custom-breakdowns`
* pref. If not found, returns an empty object.
*
* @param {String} name
* @return {Object}
*/
exports.breakdownNameToSpec = function (name) {
let customBreakdowns = exports.getCustomBreakdowns();
// If breakdown is already a breakdown, use it
if (typeof name === "object") {
return name;
}
// If it's in our custom breakdowns, use it
else if (name in customBreakdowns) {
return customBreakdowns[name];
}
// If breakdown name is in our presets, use that
else if (name in breakdowns) {
return breakdowns[name].breakdown;
}
return Object.create(null);
};
/**
* Returns an array of objects with the unique key `name` and `displayName` for
* each breakdown for dominator trees.
*
* @return {Array<Object>}
*/
exports.getDominatorTreeBreakdownDisplayData = function () {
return exports.getDominatorTreeBreakdownNames().map(({ name, tooltip }) => {
// If it's a preset use the display name value
let preset = dominatorTreeBreakdowns[name];
let displayName = name;
if (preset && preset.displayName) {
displayName = preset.displayName;
}
return { name, tooltip, displayName };
});
};
/**
* Returns an array of the unique names for each breakdown in
* presets and custom pref.
*
* @return {Array<Breakdown>}
*/
exports.getDominatorTreeBreakdownNames = function () {
let custom = exports.getCustomDominatorTreeBreakdowns();
return Object.keys(Object.assign({}, dominatorTreeBreakdowns, custom))
.map(key => {
return dominatorTreeBreakdowns[key]
? { name: key, tooltip: dominatorTreeBreakdowns[key].tooltip }
: { name: key };
});
};
/**
* Returns custom breakdowns defined in `devtools.memory.custom-dominator-tree-breakdowns` pref.
* Returns custom displays defined in `devtools.memory.custom-census-displays`
* pref.
*
* @return {Object}
*/
exports.getCustomDominatorTreeBreakdowns = function () {
let customBreakdowns = Object.create(null);
try {
customBreakdowns = JSON.parse(Preferences.get(CUSTOM_DOMINATOR_TREE_BREAKDOWN_PREF)) || Object.create(null);
} catch (e) {
DevToolsUtils.reportException(
`String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`);
}
return customBreakdowns;
exports.getCustomCensusDisplays = function () {
return getCustomDisplaysHelper(CUSTOM_CENSUS_DISPLAY_PREF);
};
/**
* Converts a dominator tree breakdown preset name, like "allocationStack", and
* returns the spec for the breakdown. Also checks properties of keys in the
* `devtools.memory.custom-breakdowns` pref. If not found, returns an empty
* object.
* Returns custom displays defined in
* `devtools.memory.custom-dominator-tree-displays` pref.
*
* @param {String} name
* @return {Object}
*/
exports.dominatorTreeBreakdownNameToSpec = function (name) {
let customBreakdowns = exports.getCustomDominatorTreeBreakdowns();
// If breakdown is already a breakdown, use it.
if (typeof name === "object") {
return name;
}
// If it's in our custom breakdowns, use it.
if (name in customBreakdowns) {
return customBreakdowns[name];
}
// If breakdown name is in our presets, use that.
if (name in dominatorTreeBreakdowns) {
return dominatorTreeBreakdowns[name].breakdown;
}
return Object.create(null);
exports.getCustomDominatorTreeDisplays = function () {
return getCustomDisplaysHelper(CUSTOM_DOMINATOR_TREE_DISPLAY_PREF);
};
/**
@ -389,61 +264,19 @@ exports.createSnapshot = function createSnapshot(state) {
};
/**
* Takes two objects and compares them deeply, returning
* a boolean indicating if they're equal or not. Used for breakdown
* comparison.
* Return true if the census is up to date with regards to the current filtering
* and requested display, false otherwise.
*
* @param {Any} obj1
* @param {Any} obj2
* @return {Boolean}
*/
const breakdownEquals = exports.breakdownEquals = function (obj1, obj2) {
let type1 = typeof obj1;
let type2 = typeof obj2;
// Quick checks
if (type1 !== type2 || (Array.isArray(obj1) !== Array.isArray(obj2))) {
return false;
}
if (obj1 === obj2) {
return true;
}
if (Array.isArray(obj1)) {
if (obj1.length !== obj2.length) { return false; }
return obj1.every((_, i) => exports.breakdownEquals(obj[1], obj2[i]));
}
else if (type1 === "object") {
let k1 = Object.keys(obj1);
let k2 = Object.keys(obj2);
if (k1.length !== k2.length) {
return false;
}
return k1.every(k => exports.breakdownEquals(obj1[k], obj2[k]));
}
return false;
};
/**
* Return true if the census is up to date with regards to the current
* inversion/filtering/breakdown, false otherwise.
*
* @param {Boolean} inverted
* @param {String} filter
* @param {Object} breakdown
* @param {censusDisplayModel} display
* @param {censusModel} census
*
* @returns {Boolean}
*/
exports.censusIsUpToDate = function (inverted, filter, breakdown, census) {
exports.censusIsUpToDate = function (filter, display, census) {
return census
&& inverted === census.inverted
&& filter === census.filter
&& breakdownEquals(breakdown, census.breakdown);
&& display === census.display;
};
/**

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

@ -108,8 +108,8 @@ pref("devtools.debugger.ui.variables-searchbox-visible", false);
// Enable the Memory tools
pref("devtools.memory.enabled", false);
pref("devtools.memory.custom-breakdowns", "{}");
pref("devtools.memory.custom-dominator-tree-breakdowns", "{}");
pref("devtools.memory.custom-census-displays", "{}");
pref("devtools.memory.custom-dominator-tree-displays", "{}");
// Enable the Performance tools
pref("devtools.performance.enabled", true);

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

@ -87,7 +87,7 @@ html, body, #app, #memory-tool {
margin-inline-end: 5px;
}
.devtools-toolbar > .toolbar-group > label.breakdown-by > span {
.devtools-toolbar > .toolbar-group > label.display-by > span {
margin-inline-end: 5px;
}