Bug 929826 - Improve about:memory's UI for the multi-process case. r=johns.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 22 Oct 2013 22:27:58 -0700
changeset 166897 c79088ab4e0c3c44a3352610effae537dc5b698b
parent 166896 53dd3aa9be7e239e1a0cac0ad6262b182685b6bb
child 166898 44de05b3239bac97f65da0d68462baa2a0ed3042
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohns
bugs929826
milestone27.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 929826 - Improve about:memory's UI for the multi-process case. r=johns.
toolkit/components/aboutmemory/content/aboutMemory.css
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/test_aboutmemory.xul
toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
--- a/toolkit/components/aboutmemory/content/aboutMemory.css
+++ b/toolkit/components/aboutmemory/content/aboutMemory.css
@@ -57,23 +57,34 @@ div.opsRowLabel {
 div.non-verbose pre.entries {
   overflow-x: hidden;
   text-overflow: ellipsis;
 }
 
 h1 {
   padding: 0;
   margin: 0;
+  display: inline;  /* allow subsequent text to the right of the heading */
 }
 
 h2 {
   background: #ddd;
   padding-left: .1em;
 }
 
+h3 {
+  display: inline;  /* allow subsequent text to the right of the heading */
+}
+
+a.upDownArrow {
+  font-size: 130%;
+  text-decoration: none;
+  -moz-user-select: none;  /* no need to include this when cutting+pasting */
+}
+
 .accuracyWarning {
   color: #d22;
 }
 
 .badInputWarning {
   color: #f00;
 }
 
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -880,17 +880,17 @@ function appendAboutMemoryMain(aProcessR
     return 0;
   });
 
   // Generate output for each process.
   for (let i = 0; i < processes.length; i++) {
     let process = processes[i];
     let section = appendElement(gMain, 'div', 'section');
 
-    appendProcessAboutMemoryElements(section, process,
+    appendProcessAboutMemoryElements(section, i, process,
                                      pcollsByProcess[process]._trees,
                                      pcollsByProcess[process]._degenerates,
                                      pcollsByProcess[process]._heapTotal,
                                      aHasMozMallocUsableSize);
   }
 }
 
 /**
@@ -1303,30 +1303,56 @@ function appendWarningElements(aP, aHasK
   }
 }
 
 /**
  * Appends the about:memory elements for a single process.
  *
  * @param aP
  *        The parent DOM node.
+ * @param aN
+ *        The number of the process, starting at 0.
  * @param aProcess
  *        The name of the process.
  * @param aTrees
  *        The table of non-degenerate trees for this process.
  * @param aDegenerates
  *        The table of degenerate trees for this process.
  * @param aHasMozMallocUsableSize
  *        Boolean indicating if moz_malloc_usable_size works.
  * @return The generated text.
  */
-function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates,
-                                          aHeapTotal, aHasMozMallocUsableSize)
+function appendProcessAboutMemoryElements(aP, aN, aProcess, aTrees,
+                                          aDegenerates, aHeapTotal,
+                                          aHasMozMallocUsableSize)
 {
-  appendElementWithText(aP, "h1", "", aProcess + "\n\n");
+  const kUpwardsArrow   = "\u2191",
+        kDownwardsArrow = "\u2193";
+
+  let appendLink = function(aHere, aThere, aArrow) {
+    let link = appendElementWithText(aP, "a", "upDownArrow", aArrow);
+    link.href = "#" + aThere + aN;
+    link.id = aHere + aN;
+    link.title = "Go to the " + aThere + " of " + aProcess;
+    link.style = "text-decoration: none";
+
+    // This jumps to the anchor without the page location getting the anchor
+    // name tacked onto its end, which is what happens with a vanilla link.
+    link.addEventListener("click", function(event) {
+      document.documentElement.scrollTop =
+        document.querySelector(event.target.href).offsetTop;
+      event.preventDefault();
+    }, false);
+
+    // This gives nice spacing when we copy and paste.
+    appendElementWithText(aP, "span", "", "\n");
+  }
+
+  appendElementWithText(aP, "h1", "", aProcess);
+  appendLink("start", "end", kDownwardsArrow);
 
   // We'll fill this in later.
   let warningsDiv = appendElement(aP, "div", "accuracyWarning");
 
   // The explicit tree.
   let hasKnownHeapAllocated;
   {
     let treeName = "explicit";
@@ -1342,17 +1368,17 @@ function appendProcessAboutMemoryElement
       hasKnownHeapAllocated =
         aDegenerates &&
         addHeapUnclassifiedNode(t, aDegenerates["heap-allocated"], aHeapTotal);
       sortTreeAndInsertAggregateNodes(t._amount, t);
       t._description = explicitTreeDescription;
       appendTreeElements(pre, t, aProcess, "");
       delete aTrees[treeName];
     }
-    appendTextNode(aP, "\n");  // gives nice spacing when we cut and paste
+    appendTextNode(aP, "\n");  // gives nice spacing when we copy and paste
   }
 
   // Fill in and sort all the non-degenerate other trees.
   let otherTrees = [];
   for (let unsafeName in aTrees) {
     let t = aTrees[unsafeName];
     assert(!t._isDegenerate, "tree is degenerate");
     fillInTree(t);
@@ -1383,23 +1409,26 @@ function appendProcessAboutMemoryElement
     appendTreeElements(pre, t, aProcess, "");
     appendTextNode(pre, "\n");  // blank lines after non-degenerate trees
   }
   for (let i = 0; i < otherDegenerates.length; i++) {
     let t = otherDegenerates[i];
     let padText = pad("", maxStringLength - t.toString().length, ' ');
     appendTreeElements(pre, t, aProcess, padText);
   }
-  appendTextNode(aP, "\n");  // gives nice spacing when we cut and paste
+  appendTextNode(aP, "\n");  // gives nice spacing when we copy and paste
 
   // Add any warnings about inaccuracies due to platform limitations.
   // These must be computed after generating all the text.  The newlines give
-  // nice spacing if we cut+paste into a text buffer.
+  // nice spacing if we copy+paste into a text buffer.
   appendWarningElements(warningsDiv, hasKnownHeapAllocated,
                         aHasMozMallocUsableSize);
+
+  appendElementWithText(aP, "h3", "", "End of " + aProcess);
+  appendLink("end", "start", kUpwardsArrow);
 }
 
 /**
  * Determines if a number has a negative sign when converted to a string.
  * Works even for -0.
  *
  * @param aN
  *        The number.
@@ -1512,17 +1541,17 @@ function pad(aS, aN, aC)
   for (let i = 0; i < n2; i++) {
     padding += aC;
   }
   return padding + aS;
 }
 
 // There's a subset of the Unicode "light" box-drawing chars that is widely
 // implemented in terminals, and this code sticks to that subset to maximize
-// the chance that cutting and pasting about:memory output to a terminal will
+// the chance that copying and pasting about:memory output to a terminal will
 // work correctly.
 const kHorizontal                   = "\u2500",
       kVertical                     = "\u2502",
       kUpAndRight                   = "\u2514",
       kUpAndRight_Right_Right       = "\u2514\u2500\u2500",
       kVerticalAndRight             = "\u251c",
       kVerticalAndRight_Right_Right = "\u251c\u2500\u2500",
       kVertical_Space_Space         = "\u2502  ";
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -274,16 +274,17 @@ 888.00 MB ── explicit\n\
 500.00 MB ── heap-allocated\n\
 100.00 MB ── heap-unallocated\n\
 222.00 MB ── other2\n\
       777 ── other3\n\
       888 ── other4 [2]\n\
    45.67% ── perc1\n\
   100.00% ── perc2\n\
 \n\
+End of Main Process\n\
 4th\n\
 \n\
 WARNING: the following values are negative or unreasonably large.\n\
 \n\
     explicit/js/compartment(http://too-big.com/)/stuff\n\
     explicit/(2 tiny)\n\
     explicit/(2 tiny)/neg1\n\
     explicit/(2 tiny)/heap-unclassified\n\
@@ -309,47 +310,50 @@ Other Measurements\n\
   -0.00 MB ── other1 [?!]\n\
 -222.00 MB ── other2 [?!]\n\
       -333 ── other3 [?!]\n\
       -444 ── other4 [?!]\n\
     -5.55% ── other5 [?!]\n\
    666.66% ── other6\n\
  200.00 MB ── resident\n\
 \n\
+End of 4th\n\
 3rd\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 777.00 MB (100.0%) -- explicit\n\
 └──777.00 MB (100.0%) -- a\n\
    ├──444.00 MB (57.14%) ── c\n\
    └──333.00 MB (42.86%) ── b\n\
 \n\
 Other Measurements\n\
 \n\
   1.00 MB ── other1\n\
 100.00 MB ── resident\n\
 \n\
+End of 3rd\n\
 2nd\n\
 Explicit Allocations\n\
 \n\
 1,000.00 MB (100.0%) -- explicit\n\
 ├────499.00 MB (49.90%) ── a/b/c [3]\n\
 ├────200.00 MB (20.00%) ── compartment(compartment-url)\n\
 ├────200.00 MB (20.00%) ── flip/the/backslashes\n\
 └────101.00 MB (10.10%) ── heap-unclassified\n\
 \n\
 Other Measurements\n\
 \n\
 1,000.00 MB ── heap-allocated\n\
   100.00 MB ── heap-unallocated\n\
   666.00 MB ── other0\n\
   111.00 MB ── other1\n\
 \n\
+End of 2nd\n\
 5th\n\
 \n\
 WARNING: the following values are negative or unreasonably large.\n\
 \n\
     explicit/(3 tiny)/a/neg2\n\
     explicit/(3 tiny)/a/neg1\n\
     explicit/(3 tiny)/b/c\n\
     explicit/(3 tiny)/b/c/d\n\
@@ -372,16 +376,17 @@ 99.95 MB (100.0%) -- explicit\n\
        └──-0.04 MB (-0.04%) -- d [?!]\n\
           ├───0.02 MB (00.02%) ── e\n\
           └──-0.06 MB (-0.06%) ── f [?!]\n\
 \n\
 Other Measurements\n\
 \n\
 100.00 MB ── heap-allocated\n\
 \n\
+End of 5th\n\
 ";
 
   let amvExpectedText =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 653,876,224 B (100.0%) -- explicit\n\
@@ -441,16 +446,17 @@ 931,135,488 B ── explicit\n\
 524,288,000 B ── heap-allocated\n\
 104,857,600 B ── heap-unallocated\n\
 232,783,872 B ── other2\n\
           777 ── other3\n\
           888 ── other4 [2]\n\
        45.67% ── perc1\n\
       100.00% ── perc2\n\
 \n\
+End of Main Process\n\
 4th\n\
 \n\
 WARNING: the following values are negative or unreasonably large.\n\
 \n\
     explicit/js/compartment(http://too-big.com/)/stuff\n\
     explicit/neg1\n\
     explicit/heap-unclassified\n\
     other1\n\
@@ -474,47 +480,50 @@ Other Measurements\n\
         -111 B ── other1 [?!]\n\
 -232,783,872 B ── other2 [?!]\n\
           -333 ── other3 [?!]\n\
           -444 ── other4 [?!]\n\
         -5.55% ── other5 [?!]\n\
        666.66% ── other6\n\
  209,715,200 B ── resident\n\
 \n\
+End of 4th\n\
 3rd\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 814,743,552 B (100.0%) -- explicit\n\
 └──814,743,552 B (100.0%) -- a\n\
    ├──465,567,744 B (57.14%) ── c\n\
    └──349,175,808 B (42.86%) ── b\n\
 \n\
 Other Measurements\n\
 \n\
   1,048,576 B ── other1\n\
 104,857,600 B ── resident\n\
 \n\
+End of 3rd\n\
 2nd\n\
 Explicit Allocations\n\
 \n\
 1,048,576,000 B (100.0%) -- explicit\n\
 ├────523,239,424 B (49.90%) ── a/b/c [3]\n\
 ├────209,715,200 B (20.00%) ── compartment(compartment-url)\n\
 ├────209,715,200 B (20.00%) ── flip/the/backslashes\n\
 └────105,906,176 B (10.10%) ── heap-unclassified\n\
 \n\
 Other Measurements\n\
 \n\
 1,048,576,000 B ── heap-allocated\n\
   104,857,600 B ── heap-unallocated\n\
   698,351,616 B ── other0\n\
   116,391,936 B ── other1\n\
 \n\
+End of 2nd\n\
 5th\n\
 \n\
 WARNING: the following values are negative or unreasonably large.\n\
 \n\
     explicit/a/neg2\n\
     explicit/a/neg1\n\
     explicit/b/c\n\
     explicit/b/c/d\n\
@@ -536,17 +545,18 @@ 104,801,280 B (100.0%) -- explicit\n\
        └──-40,960 B (-0.04%) -- d [?!]\n\
           ├───20,480 B (00.02%) ── e\n\
           └──-61,440 B (-0.06%) ── f [?!]\n\
 \n\
 Other Measurements\n\
 \n\
 104,857,600 B ── heap-allocated\n\
 \n\
-"
+End of 5th\n\
+";
 
   function finish()
   {
     // Unregister fake reporters and re-register the real reporters, just in
     // case subsequent tests rely on them.
     for (let i = 0; i < fakeReporters.length; i++) {
       mgr.unregisterReporter(fakeReporters[i]);
     }
@@ -562,17 +572,17 @@ 104,857,600 B ── heap-allocated\n\
   // expect.  This tests the output in general and also that the cutting and
   // pasting works as expected.
   function test(aFrameId, aVerbose, aExpected, aNext) {
     SimpleTest.executeSoon(function() {
       ok(document.title === "about:memory", "document.title is correct");
       let mostRecentActual;
       let frame = document.getElementById(aFrameId);
       frame.focus();
-        
+
       // Set the verbose checkbox value and click the go button.
       let doc = frame.contentWindow.document;
       let measureButton = doc.getElementById("measureButton");
       let verbose = doc.getElementById("verbose");
       verbose.checked = aVerbose;
       measureButton.click();
 
       SimpleTest.waitForClipboard(
@@ -583,19 +593,19 @@ 104,857,600 B ── heap-allocated\n\
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match for " + aFrameId);
           dump("******EXPECTED******\n");
-          dump(aExpected);
+          dump("<<<" + aExpected + ">>>\n");
           dump("*******ACTUAL*******\n");
-          dump(mostRecentActual);
+          dump("<<<" + mostRecentActual + ">>>\n");
           dump("********************\n");
           finish();
         }
       );
     });
   }
 
   SimpleTest.waitForFocus(function() {
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
@@ -172,16 +172,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── i2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ j\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let acCollapsedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -196,16 +197,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── i2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ j\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let alExpandedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -222,16 +224,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── i2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ j\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let aCollapsedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -242,16 +245,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── i2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ j\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let hCollapsedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -260,16 +264,17 @@ 250.00 MB (100.0%) -- explicit\n\
 ├───19.00 MB (07.60%) ++ h\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ j\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let jExpandedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -280,16 +285,17 @@ 250.00 MB (100.0%) -- explicit\n\
 └────0.80 MB (00.32%) -- j\n\
      ├──0.50 MB (00.20%) ── k\n\
      └──0.30 MB (00.12%) ── k2\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   // The important thing here is that two values have been swapped.
   // explicit/h/i should remain collapsed, and explicit/j/k should remain
   // expanded.  See bug 724863.
   let updatedExpected =
 "\
 Main Process\n\
@@ -303,16 +309,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── k2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ h\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let aExpandedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -329,16 +336,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── k2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ h\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let acExpandedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -357,16 +365,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── k2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ h\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   let alCollapsedExpected =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
@@ -383,16 +392,17 @@ 250.00 MB (100.0%) -- explicit\n\
 │   └───9.00 MB (03.60%) ── k2\n\
 ├───10.00 MB (04.00%) ── heap-unclassified\n\
 └────0.80 MB (00.32%) ++ h\n\
 \n\
 Other Measurements\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process\n\
 ";
 
   // Test the following cases:
   // - explicit/a/c is significant, we collapse it, it's unchanged upon
   //   update, we re-expand it
   // - explicit/a/l is insignificant, we expand it, it's unchanged upon
   //   update, we re-collapse it
   // - explicit/a is significant, we collapse it (which hides its
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
@@ -154,17 +154,17 @@
     // Because the file load is async, we don't know when it will finish and
     // the output will show up.  So we poll.
     function copyPasteAndCheck() {
       // Copy and paste frame contents, and filter out non-deterministic
       // differences.
       synthesizeKey("A", {accelKey: true});
       synthesizeKey("C", {accelKey: true});
       let actual = SpecialPowers.getClipboardData("text/unicode");
-      actual = actual.replace(/\(pid \d+\)/, "(pid NNN)");
+      actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)");
 
       if (actual === aExpected) {
         SimpleTest.ok(true, "Clipboard has the expected contents");
         aNext();
       } else {
         numFailures++;
         if (numFailures === maxFailures) {
           ok(false, "pasted text doesn't match");
@@ -199,16 +199,17 @@ Explicit-only process\n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 100,000 B (100.0%) -- explicit\n\
 └──100,000 B (100.0%) ── a/b\n\
 \n\
 Other Measurements\n\
 \n\
+End of Explicit-only process\n\
 Main Process (pid NNN)\n\
 Explicit Allocations\n\
 \n\
 262,144,000 B (100.0%) -- explicit\n\
 ├──209,715,200 B (80.00%) ── heap-unclassified\n\
 └───52,428,800 B (20.00%) ── a/b\n\
 \n\
 Other Measurements\n\
@@ -232,29 +233,31 @@ 1,024 B (100.0%) -- rss\n\
 1,024 B (100.0%) -- size\n\
 └──1,024 B (100.0%) ── a\n\
 \n\
 1,024 B (100.0%) -- swap\n\
 └──1,024 B (100.0%) ── a\n\
 \n\
 262,144,000 B ── heap-allocated\n\
 \n\
+End of Main Process (pid NNN)\n\
 Other-only process\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 Other Measurements\n\
 \n\
 200,000 B (100.0%) -- a\n\
 ├──100,000 B (50.00%) ── b\n\
 └──100,000 B (50.00%) ── c\n\
 \n\
 500,000 B ── heap-allocated\n\
 \n\
+End of Other-only process\n\
 ";
 
   let expectedGood2 =
 "\
 Main Process (pid NNN)\n\
 Explicit Allocations\n\
 \n\
 262,144,000 B (100.0%) -- explicit\n\
@@ -264,22 +267,24 @@ 262,144,000 B (100.0%) -- explicit\n\
 Other Measurements\n\
 \n\
 314,572 B (100.0%) -- other\n\
 ├──209,715 B (66.67%) ── a\n\
 └──104,857 B (33.33%) ── b\n\
 \n\
 262,144,000 B ── heap-allocated\n\
 \n\
+End of Main Process (pid NNN)\n\
 ";
 
   // This is the output for a malformed data file.
   let expectedBad =
 "\
-Invalid memory report(s): missing 'hasMozMallocUsableSize' property";
+Invalid memory report(s): missing 'hasMozMallocUsableSize' property\
+";
 
   // This is the output for a diff.
   let expectedDiff =
 "\
 P\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
@@ -289,43 +294,47 @@ Explicit Allocations\n\
 ├───────-6 B (00.06%) ── spell-check [2]\n\
 └────────1 B (-0.01%) ── xpcom/category-manager\n\
 \n\
 Other Measurements\n\
 \n\
 3,000 B ── canvas-2d-pixel-bytes [2] [+]\n\
  -100 B ── foobar [-]\n\
 \n\
+End of P\n\
 P2 (pid NNN)\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 Other Measurements\n\
 \n\
 11 B ── z 0xNNN\n\
 \n\
+End of P2 (pid NNN)\n\
 P3\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 Other Measurements\n\
 \n\
 -55 B ── p3 [-]\n\
 \n\
+End of P3\n\
 P4\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 Other Measurements\n\
 \n\
 66 B ── p4 [+]\n\
 \n\
+End of P4\n\
 ";
 
   let frames = [
     // This loads a pre-existing file that is valid.
     { filename: "memory-reports-good.json", expected: expectedGood, dumpFirst: false },
 
     // This dumps to a file and then reads it back in.
     { filename: "memory-reports-dumped.json.gz", expected: expectedGood2, dumpFirst: true },
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
@@ -94,16 +94,17 @@ Explicit-only process\n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 0.10 MB (100.0%) -- explicit\n\
 └──0.10 MB (100.0%) ── a/b\n\
 \n\
 Other Measurements\n\
 \n\
+End of Explicit-only process\n\
 Main Process (pid NNN)\n\
 Explicit Allocations\n\
 \n\
 250.00 MB (100.0%) -- explicit\n\
 ├──200.00 MB (80.00%) ── heap-unclassified\n\
 └───50.00 MB (20.00%) ── a/b\n\
 \n\
 Other Measurements\n\
@@ -127,29 +128,31 @@ 0.00 MB (100.0%) -- rss\n\
 0.00 MB (100.0%) -- size\n\
 └──0.00 MB (100.0%) ── a\n\
 \n\
 0.00 MB (100.0%) -- swap\n\
 └──0.00 MB (100.0%) ── a\n\
 \n\
 250.00 MB ── heap-allocated\n\
 \n\
+End of Main Process (pid NNN)\n\
 Other-only process\n\
 \n\
 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\
 Explicit Allocations\n\
 \n\
 Other Measurements\n\
 \n\
 0.19 MB (100.0%) -- a\n\
 ├──0.10 MB (50.00%) ── b\n\
 └──0.10 MB (50.00%) ── c\n\
 \n\
 0.48 MB ── heap-allocated\n\
 \n\
+End of Other-only process\n\
 ";
 
   // This is the output for a malformed data file.
   let expectedBad =
 "\
 Invalid memory report(s): missing 'hasMozMallocUsableSize' property";
 
   let frames = [