Bug 1218560 - Fix heap tree view item rendering when using an allocation stack breakdown; r=jsantell
--- a/devtools/client/memory/components/tree-item.js
+++ b/devtools/client/memory/components/tree-item.js
@@ -1,27 +1,56 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
+const { isSavedFrame } = require("devtools/shared/DevToolsUtils");
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+
const INDENT = 10;
+const MAX_SOURCE_LENGTH = 200;
+
/**
* An arrow that displays whether its node is expanded (▼) or collapsed
* (▶). When its node has no children, it is hidden.
*/
const TreeItem = module.exports = createClass({
displayName: "tree-item",
render() {
let { item, depth, arrow, focused } = this.props;
return dom.div({ className: "heap-tree-item", style: { marginLeft: depth * INDENT }},
arrow,
- dom.span({ className: "heap-tree-item-name" }, item.name),
+ dom.span({ className: "heap-tree-item-name" }, this.toLabel(item.name)),
dom.span({ className: "heap-tree-item-bytes" }, item.bytes),
dom.span({ className: "heap-tree-item-count" }, item.count),
dom.span({ className: "heap-tree-item-total-bytes" }, item.totalBytes),
dom.span({ className: "heap-tree-item-total-count" }, item.totalCount)
);
+ },
+
+ /**
+ * Convert the given item name to a renderable.
+ */
+ toLabel(name) {
+ return isSavedFrame(name)
+ ? this.savedFrameToLabel(name)
+ : String(name);
+ },
+
+ /**
+ * Given a SavedFrame object, convert it to a renderable.
+ */
+ savedFrameToLabel(frame) {
+ return [
+ dom.span({ className: "heap-tree-item-function-display-name" },
+ frame.functionDisplayFrame || ""),
+ dom.span({ className: "heap-tree-item-at" }, "@"),
+ dom.span({ className: "heap-tree-item-source" }, frame.source.slice(0, MAX_SOURCE_LENGTH)),
+ dom.span({ className: "heap-tree-item-colon" }, ":"),
+ dom.span({ className: "heap-tree-item-line" }, frame.line),
+ dom.span({ className: "heap-tree-item-colon" }, ":"),
+ dom.span({ className: "heap-tree-item-column" }, frame.column)
+ ];
}
});
--- a/devtools/client/memory/initializer.js
+++ b/devtools/client/memory/initializer.js
@@ -14,21 +14,29 @@ const { Provider } = require("devtools/c
const App = createFactory(require("devtools/client/memory/app"));
const Store = require("devtools/client/memory/store");
/**
* The current target, toolbox, MemoryFront, and HeapAnalysesClient, set by this tool's host.
*/
var gToolbox, gTarget, gFront, gHeapAnalysesClient;
+/**
+ * Globals set by initialize().
+ */
+var gRoot, gStore, gApp, gProvider;
+
function initialize () {
return Task.spawn(function*() {
- let root = document.querySelector("#app");
- let store = Store();
- let app = createElement(App, { front: gFront, heapWorker: gHeapAnalysesClient });
- let provider = createElement(Provider, { store }, app);
- render(provider, root);
+ gRoot = document.querySelector("#app");
+ gStore = Store();
+ gApp = createElement(App, {
+ front: gFront,
+ heapWorker: gHeapAnalysesClient
+ });
+ gProvider = createElement(Provider, { store: gStore }, gApp);
+ render(gProvider, gRoot);
});
}
function destroy () {
return Task.spawn(function*(){});
}
--- a/devtools/client/memory/test/browser/browser.ini
+++ b/devtools/client/memory/test/browser/browser.ini
@@ -1,7 +1,9 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
+ doc_steady_allocation.html
+[browser_memory_allocationStackBreakdown_01.js]
[browser_memory_transferHeapSnapshot_e10s_01.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/test/browser/browser_memory_allocationStackBreakdown_01.js
@@ -0,0 +1,39 @@
+/* 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.
+
+"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 { setBreakdown } = require("devtools/client/memory/actions/breakdown");
+const { toggleInverted } = require("devtools/client/memory/actions/inverted");
+
+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 heapWorker = panel.panelWin.gHeapAnalysesClient;
+ const front = panel.panelWin.gFront;
+ const { getState, dispatch } = panel.panelWin.gStore;
+
+ dispatch(toggleInverted());
+ ok(getState().inverted, true);
+
+ dispatch(setBreakdown(breakdowns.allocationStack.breakdown));
+ is(getState().breakdown.by, "allocationStack");
+
+ yield dispatch(toggleRecordingAllocationStacks(front));
+ ok(getState().allocations.recording);
+
+ // Let some allocations build up.
+ yield waitForTime(500);
+
+ yield dispatch(takeSnapshotAndCensus(front, heapWorker));
+
+ const doc = panel.panelWin.document;
+ ok(doc.querySelector(".heap-tree-item-function-display-name"),
+ "Should have rendered some allocation stack tree items");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/test/browser/doc_steady_allocation.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <script>
+ var objects = window.objects = [];
+
+ var allocate = this.allocate = function allocate() {
+ for (var i = 0; i < 100; i++)
+ objects.push({});
+ setTimeout(allocate, 10);
+ }
+
+ allocate();
+ </script>
+ </body>
+</html>
--- a/devtools/client/memory/test/browser/head.js
+++ b/devtools/client/memory/test/browser/head.js
@@ -5,16 +5,20 @@
// Load the shared test helpers into this compartment.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.prefs.setBoolPref("devtools.memory.enabled", true);
+function dumpn(msg) {
+ dump("MEMORY: " + msg + "\n");
+}
+
/**
* Open the memory panel for the given tab.
*/
this.openMemoryPanel = Task.async(function* (tab) {
info("Opening memory panel.");
const target = TargetFactory.forTab(tab);
const toolbox = yield gDevTools.showToolbox(target, "memory");
info("Memory panel shown successfully.");
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -813,8 +813,15 @@ exports.openFileStream = function (fileP
exports.isGenerator = function (fn) {
return typeof fn === "function" && fn.isGenerator();
};
exports.isPromise = function (p) {
return p && typeof p.then === "function";
};
+
+/**
+ * Return true if `thing` is a SavedFrame, false otherwise.
+ */
+exports.isSavedFrame = function (thing) {
+ return Object.prototype.toString.call(thing) === "[object SavedFrame]";
+};