Bug 1218560 - Fix heap tree view item rendering when using an allocation stack breakdown; r=jsantell
☠☠ backed out by 3af8b8e56340 ☠ ☠
authorNick Fitzgerald <fitzgen@gmail.com>
Mon, 26 Oct 2015 14:58:03 -0700
changeset 269618 15acea2927e2103a21ee2e350a92bf34181e7695
parent 269617 e3b623fd16bf26c954de67eb56757b8b5a94cbcd
child 269619 9a0ee09782a32d2ec4949f3e2e8b930c84e9c5e7
push id29586
push usercbook@mozilla.com
push dateTue, 27 Oct 2015 09:52:52 +0000
treeherdermozilla-central@e36bf6850a93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell
bugs1218560
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1218560 - Fix heap tree view item rendering when using an allocation stack breakdown; r=jsantell
devtools/client/memory/components/tree-item.js
devtools/client/memory/initializer.js
devtools/client/memory/test/browser/browser.ini
devtools/client/memory/test/browser/browser_memory_allocationStackBreakdown_01.js
devtools/client/memory/test/browser/doc_steady_allocation.html
devtools/client/memory/test/browser/head.js
devtools/shared/DevToolsUtils.js
--- 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]";
+};