Bug 1217158 - Aggregate total counts/bytes in CensusTreeNode; r=jsantell
☠☠ backed out by c4c32be81581 ☠ ☠
authorNick Fitzgerald <fitzgen@gmail.com>
Wed, 21 Oct 2015 12:53:00 -0400
changeset 304045 be72055dc7acd57df7e17de9b39237692c9d2b65
parent 304044 3e148d8f5e0522a64968dc04562e6b1fd17ecbdf
child 304046 8a3d24ccd4928ba5f7557b5a052eed658c709cea
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell
bugs1217158
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 1217158 - Aggregate total counts/bytes in CensusTreeNode; r=jsantell
devtools/client/memory/test/mochitest/test_census-view-01.html
devtools/shared/heapsnapshot/census-tree-node.js
devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-01.js
devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js
devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-03.js
devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-04.js
devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-05.js
--- a/devtools/client/memory/test/mochitest/test_census-view-01.html
+++ b/devtools/client/memory/test/mochitest/test_census-view-01.html
@@ -41,24 +41,24 @@ window.onload = function() {
     "scripts": { bytes: 20, count: 2 },
     "other": {
       "js::Shape": { bytes: 30, count: 3 },
       "js::Shape2": { bytes: 40, count: 4 }
     },
   };
 
   const EXPECTED_ROWS = [
+    { level: 0, name: "other", bytes: 0, count: 0 },
+        { level: 1, name: "js::Shape2", bytes: 40, count: 4, },
+        { level: 1, name: "js::Shape", bytes: 30, count: 3, },
+    { level: 0, name: "objects", bytes: 0, count: 0 },
+        { level: 1, name: "Array", bytes: 20, count: 2, },
+        { level: 1, name: "Function", bytes: 10, count: 1, },
     { level: 0, name: "scripts", bytes: 20, count: 2, },
     { level: 0, name: "strings", bytes: 10, count: 1, },
-    { level: 0, name: "objects" },
-        { level: 1, name: "Array", bytes: 20, count: 2, },
-        { level: 1, name: "Function", bytes: 10, count: 1, },
-    { level: 0, name: "other" },
-        { level: 1, name: "js::Shape2", bytes: 40, count: 4, },
-        { level: 1, name: "js::Shape", bytes: 30, count: 3, },
   ];
   var censusTreeNode = censusReportToCensusTreeNode(BREAKDOWN, REPORT);
 
   var view = new CensusView({
     censusTreeNode: censusTreeNode,
     hidden: true
   });
 
@@ -79,24 +79,24 @@ window.onload = function() {
 
     is(el.style.MozMarginStart, (INDENTATION * expected.level) + "px",
       `correct indentation for ${expected.name}`);
 
     if ("bytes" in expected) {
       is(bytesEl.innerHTML, String(expected.bytes),
         `correct bytes "${expected.bytes}" in heap tree`);
     } else {
-      ok(!bytesEl, "no bytes correctly displayed for ${expected.name}");
+      ok(!bytesEl, `no bytes correctly displayed for ${expected.name}`);
     }
 
     if ("count" in expected) {
       is(countEl.innerHTML, String(expected.count),
         `correct count "${expected.count}" in heap tree`);
     } else {
-      ok(!countEl, "no count correctly displayed for ${expected.name}");
+      ok(!countEl, `no count correctly displayed for ${expected.name}`);
     }
   }
 
   SimpleTest.finish();
 };
 </script>
 </pre>
 </body>
--- a/devtools/shared/heapsnapshot/census-tree-node.js
+++ b/devtools/shared/heapsnapshot/census-tree-node.js
@@ -259,42 +259,45 @@ function values(cache) {
 /**
  * We have finished adding children to the CensusTreeNode subtree for the
  * current sub-report. Make sure that the children are sorted for every node in
  * the subtree.
  *
  * @overrides Visitor.prototype.exit
  */
 CensusTreeNodeVisitor.prototype.exit = function (breakdown, report, edge) {
-  const top = this._nodeStack.pop();
-  if (top.children) {
-    top.children.sort(compareByBytes);
-  }
+  // Ensure all children are sorted and have their counts/bytes aggregated. We
+  // only need to consider cache children here, because other children
+  // correspond to other sub-reports and we already fixed them up in an earlier
+  // invocation of `exit`.
 
-  const cache = this._frameCacheStack.pop();
-  const toSort = values(cache);
-  while (toSort.length) {
-    const { node, children } = toSort.pop();
-    if (!node.children) {
-      continue;
+  function dfs(node, childrenCache) {
+    if (childrenCache) {
+      const childValues = values(childrenCache);
+      for (let i = 0, length = childValues.length; i < length; i++) {
+        dfs(childValues[i].node, childValues[i].children);
+      }
     }
 
-    if (node !== top) {
-      node.children.sort(compareByBytes);
-    }
+    node.totalCount = node.count;
+    node.totalBytes = node.bytes;
 
-    if (!children) {
-      continue;
-    }
+    if (node.children) {
+      node.children.sort(compareByTotalBytes);
 
-    const newlyNeedSorting = values(children);
-    for (let i = 0, length = newlyNeedSorting.length; i < length; i++) {
-      toSort.push(newlyNeedSorting[i]);
+      for (let i = 0, length = node.children.length; i < length; i++) {
+        node.totalCount += node.children[i].totalCount;
+        node.totalBytes += node.children[i].totalBytes;
+      }
     }
   }
+
+  const top = this._nodeStack.pop();
+  const cache = this._frameCacheStack.pop();
+  dfs(top, cache);
 };
 
 /**
  * @overrides Visitor.prototype.count
  */
 CensusTreeNodeVisitor.prototype.count = function (breakdown, report, edge) {
   const node = this._nodeStack[this._nodeStack.length - 1];
 
@@ -326,34 +329,40 @@ CensusTreeNodeVisitor.prototype.root = f
 
 /**
  * Create a single, uninitialized CensusTreeNode.
  *
  * @param {null|String|SavedFrame} name
  */
 function CensusTreeNode (name) {
   this.name = name;
-  this.bytes = undefined;
-  this.count = undefined;
+  this.bytes = 0;
+  this.totalBytes = 0;
+  this.count = 0;
+  this.totalCount = 0;
   this.children = undefined;
 }
 
 CensusTreeNode.prototype = null;
 
 /**
- * Compare the given nodes by their `bytes` properties.
+ * Compare the given nodes by their `totalBytes` properties, and breaking ties
+ * with the `bytes`, `totalCount`, and `count` properties (in that order).
  *
  * @param {CensusTreeNode} node1
  * @param {CensusTreeNode} node2
  *
  * @returns {Number}
  *          A number suitable for using with Array.prototype.sort.
  */
-function compareByBytes (node1, node2) {
-  return (node2.bytes || 0) - (node1.bytes || 0);
+function compareByTotalBytes (node1, node2) {
+  return node2.totalBytes - node1.totalBytes
+      || node2.bytes      - node1.bytes
+      || node2.totalCount - node1.totalCount
+      || node2.count      - node1.count;
 }
 
 /**
  * Takes a report from a census (`dbg.memory.takeCensus()`) and the breakdown
  * used to generate the census and returns a structure used to render
  * a tree to display the data.
  *
  * Returns a recursive "CensusTreeNode" object, looking like:
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-01.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-01.js
@@ -22,20 +22,43 @@ const REPORT = {
   "JSString": {
     "bytes": 0,
     "count": 0,
   },
 };
 
 const EXPECTED = {
   name: null,
-  bytes: undefined,
-  count: undefined,
+  bytes: 0,
+  totalBytes: 600,
+  count: 0,
+  totalCount: 60,
   children: [
-    { name: "js::Shape", bytes: 500, count: 50, children: undefined },
-    { name: "JSObject", bytes: 100, count: 10, children: undefined },
-    { name: "JSString", bytes: 0, count: 0, children: undefined },
+    {
+      name: "js::Shape",
+      bytes: 500,
+      totalBytes: 500,
+      count: 50,
+      totalCount: 50,
+      children: undefined
+    },
+    {
+      name: "JSObject",
+      bytes: 100,
+      totalBytes: 100,
+      count: 10,
+      totalCount: 10,
+      children: undefined
+    },
+    {
+      name: "JSString",
+      bytes: 0,
+      totalBytes: 0,
+      count: 0,
+      totalCount: 0,
+      children: undefined
+    },
   ],
 };
 
 function run_test() {
   compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
 }
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js
@@ -25,47 +25,85 @@ const REPORT = {
   "other": {
     "js::Shape": { bytes: 30, count: 3 },
     "js::Shape2": { bytes: 40, count: 4 }
   },
 };
 
 const EXPECTED = {
   name: null,
-  bytes: undefined,
-  count: undefined,
+  bytes: 0,
+  totalBytes: 111,
+  count: 0,
+  totalCount: 12,
   children: [
     {
+      name: "other",
+      count: 0,
+      totalCount: 7,
+      bytes: 0,
+      totalBytes: 70,
+      children: [
+        {
+          name: "js::Shape2",
+          bytes: 40,
+          totalBytes: 40,
+          count: 4,
+          totalCount: 4,
+          children: undefined
+        },
+        {
+          name: "js::Shape",
+          bytes: 30,
+          totalBytes: 30,
+          count: 3,
+          totalCount: 3,
+          children: undefined
+        },
+      ]
+    },
+    {
+      name: "objects",
+      count: 0,
+      totalCount: 3,
+      bytes: 0,
+      totalBytes: 30,
+      children: [
+        {
+          name: "Array",
+          bytes: 20,
+          totalBytes: 20,
+          count: 2,
+          totalCount: 2,
+          children: undefined
+        },
+        {
+          name: "Function",
+          bytes: 10,
+          totalBytes: 10,
+          count: 1,
+          totalCount: 1,
+          children: undefined
+        },
+      ]
+    },
+    {
       name: "strings",
       count: 1,
+      totalCount: 1,
       bytes: 10,
+      totalBytes: 10,
       children: undefined
     },
     {
       name: "scripts",
       count: 1,
+      totalCount: 1,
       bytes: 1,
+      totalBytes: 1,
       children: undefined
     },
-    {
-      name: "objects",
-      count: undefined,
-      bytes: undefined,
-      children: [
-        { name: "Array", bytes: 20, count: 2, children: undefined },
-        { name: "Function", bytes: 10, count: 1, children: undefined },
-      ]
-    },
-    {
-      name: "other",
-      count: undefined,
-      bytes: undefined,
-      children: [
-        { name: "js::Shape2", bytes: 40, count: 4, children: undefined },
-        { name: "js::Shape", bytes: 30, count: 3, children: undefined },
-      ]
-    },
   ]
 };
 
 function run_test() {
   compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
 }
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-03.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-03.js
@@ -19,28 +19,60 @@ const REPORT = {
   "other": {
     "JIT::CODE::NOW!!!": { bytes: 20, count: 2 },
     "JIT::CODE::LATER!!!": { bytes: 40, count: 4 }
   }
 };
 
 const EXPECTED = {
   name: null,
-  count: undefined,
-  bytes: undefined,
+  count: 0,
+  totalCount: 17,
+  bytes: 0,
+  totalBytes: 170,
   children: [
-    { name: "Array", bytes: 100, count: 1, children: undefined },
-    { name: "Function", bytes: 10, count: 10, children: undefined },
+    {
+      name: "Array",
+      bytes: 100,
+      totalBytes: 100,
+      count: 1,
+      totalCount: 1,
+      children: undefined
+    },
     {
       name: "other",
-      count: undefined,
-      bytes: undefined,
+      count: 0,
+      totalCount: 6,
+      bytes: 0,
+      totalBytes: 60,
       children: [
-        { name: "JIT::CODE::LATER!!!", bytes: 40, count: 4, children: undefined },
-        { name: "JIT::CODE::NOW!!!", bytes: 20, count: 2, children: undefined },
+        {
+          name: "JIT::CODE::LATER!!!",
+          bytes: 40,
+          totalBytes: 40,
+          count: 4,
+          totalCount: 4,
+          children: undefined
+        },
+        {
+          name: "JIT::CODE::NOW!!!",
+          bytes: 20,
+          totalBytes: 20,
+          count: 2,
+          totalCount: 2,
+          children: undefined
+        },
       ]
-    }
+    },
+    {
+      name: "Function",
+      bytes: 10,
+      totalBytes: 10,
+      count: 10,
+      totalCount: 10,
+      children: undefined
+    },
   ]
 };
 
 function run_test() {
   compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
 }
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-04.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-04.js
@@ -37,73 +37,93 @@ function run_test() {
     [stack3,    { bytes: 30, count: 3 }],
     [stack4,    { bytes: 40, count: 4 }],
     [stack5,    { bytes: 50, count: 5 }],
     ["noStack", { bytes: 60, count: 6 }],
   ]);
 
   const EXPECTED = {
     name: null,
-    bytes: undefined,
-    count: undefined,
+    bytes: 0,
+    totalBytes: 210,
+    count: 0,
+    totalCount: 21,
     children: [
       {
+        name: stack4.parent,
+        bytes: 0,
+        totalBytes: 100,
+        count: 0,
+        totalCount: 10,
+        children: [
+          {
+            name: stack3.parent,
+            bytes: 0,
+            totalBytes: 50,
+            count: 0,
+            totalCount: 5,
+            children: [
+              {
+                name: stack3,
+                bytes: 30,
+                totalBytes: 30,
+                count: 3,
+                totalCount: 3,
+                children: undefined
+              },
+              {
+                name: stack2,
+                bytes: 20,
+                totalBytes: 20,
+                count: 2,
+                totalCount: 2,
+                children: undefined
+              }
+            ]
+          },
+          {
+            name: stack4,
+            bytes: 40,
+            totalBytes: 40,
+            count: 4,
+            totalCount: 4,
+            children: undefined
+          },
+          {
+            name: stack1.parent,
+            bytes: 0,
+            totalBytes: 10,
+            count: 0,
+            totalCount: 1,
+            children: [
+              {
+                name: stack1,
+                bytes: 10,
+                totalBytes: 10,
+                count: 1,
+                totalCount: 1,
+                children: undefined
+              },
+            ]
+          },
+        ]
+      },
+      {
         name: "noStack",
         bytes: 60,
+        totalBytes: 60,
         count: 6,
+        totalCount: 6,
         children: undefined
       },
       {
         name: stack5,
         bytes: 50,
+        totalBytes: 50,
         count: 5,
+        totalCount: 5,
         children: undefined
       },
-      {
-        name: stack4.parent,
-        bytes: undefined,
-        count: undefined,
-        children: [
-          {
-            name: stack4,
-            bytes: 40,
-            count: 4,
-            children: undefined
-          },
-          {
-            name: stack1.parent,
-            bytes: undefined,
-            count: undefined,
-            children: [
-              {
-                name: stack1,
-                bytes: 10,
-                count: 1,
-                children: undefined
-              },
-            ]
-          },
-          {
-            name: stack3.parent,
-            bytes: undefined,
-            count: undefined,
-            children: [
-              {
-                name: stack3,
-                bytes: 30,
-                count: 3,
-                children: undefined
-              },
-              {
-                name: stack2,
-                bytes: 20,
-                count: 2,
-                children: undefined
-              }
-            ]
-          }
-        ]
-      }
     ]
   };
 
   compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
 }
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-05.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-05.js
@@ -34,67 +34,85 @@ function run_test() {
                   Baz:   { bytes: 30, count: 3 },
                   other: { bytes: 40, count: 4 }
                 }],
     ["noStack", { bytes: 50, count: 5 }],
   ]);
 
   const EXPECTED = {
     name: null,
-    bytes: undefined,
-    count: undefined,
+    bytes: 0,
+    totalBytes: 150,
+    count: 0,
+    totalCount: 15,
     children: [
       {
-        name: "noStack",
-        bytes: 50,
-        count: 5,
-        children: undefined
-      },
-      {
         name: stack.parent.parent,
-        bytes: undefined,
-        count: undefined,
+        bytes: 0,
+        totalBytes: 100,
+        count: 0,
+        totalCount: 10,
         children: [
           {
             name: stack.parent,
-            bytes: undefined,
-            count: undefined,
+            bytes: 0,
+            totalBytes: 100,
+            count: 0,
+            totalCount: 10,
             children: [
               {
                 name: stack,
-                bytes: undefined,
-                count: undefined,
+                bytes: 0,
+                totalBytes: 100,
+                count: 0,
+                totalCount: 10,
                 children: [
                   {
                     name: "other",
                     bytes: 40,
+                    totalBytes: 40,
                     count: 4,
+                    totalCount: 4,
                     children: undefined
                   },
                   {
                     name: "Baz",
                     bytes: 30,
+                    totalBytes: 30,
                     count: 3,
+                    totalCount: 3,
                     children: undefined
                   },
                   {
                     name: "Bar",
                     bytes: 20,
+                    totalBytes: 20,
                     count: 2,
+                    totalCount: 2,
                     children: undefined
                   },
                   {
                     name: "Foo",
                     bytes: 10,
+                    totalBytes: 10,
                     count: 1,
+                    totalCount: 1,
                     children: undefined
                   },
                 ]
               }
             ]
           }
         ]
-      }
+      },
+      {
+        name: "noStack",
+        bytes: 50,
+        totalBytes: 50,
+        count: 5,
+        totalCount: 5,
+        children: undefined
+      },
     ]
   };
 
   compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
 }