Bug 1217158 - Aggregate total counts/bytes in CensusTreeNode; r=jsantell
authorNick Fitzgerald <fitzgen@gmail.com>
Thu, 22 Oct 2015 09:12:36 -0700
changeset 304192 d61a6f4432d61393c47649afe20843599c05dfac
parent 304191 e5d4bd62d2342d53f9248f584f7d0219e8cfa051
child 304193 e472a823244c69201a5acab853fdd87c59f26b68
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);
 }