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 269584 15acea2927e2103a21ee2e350a92bf34181e7695
parent 269583 e3b623fd16bf26c954de67eb56757b8b5a94cbcd
child 269585 9a0ee09782a32d2ec4949f3e2e8b930c84e9c5e7
push id15893
push usernfitzgerald@mozilla.com
push dateMon, 26 Oct 2015 21:58:07 +0000
treeherderfx-team@15acea2927e2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell
bugs1218560
milestone44.0a1
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]";
+};