Bug 1035570 (part 2) - DMD: make output easier for machines to parse. r=erahm.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 05 Jun 2014 19:06:50 -0700
changeset 196365 c58334ac43628dc5225d989576c538a4ee35a70f
parent 196364 664fbd3821c72cf4ea92fa6f0014c823d5cbeddf
child 196366 92b7bbf5c1afa386d768e5c5a8cc7baa4d23100b
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerserahm
bugs1035570
milestone34.0a1
Bug 1035570 (part 2) - DMD: make output easier for machines to parse. r=erahm. There are likely to be post-processing scripts for DMD added in the future. In anticipation, this patch tweaks DMD's output to be a little more conducive to machine parsing. The basic idea is this: - Lines beginning with '#' are comments and can be ignored, as can blank lines. - All top level blocks consist of a string ending with '{', and then one or more indented lines, and then a closing '}' on its own line. Any multi-line things within a block are themselves enclosed in braces. The diff for memory/replace/dmd/test-expected.dmd shows what this looks like in practice. It's a long way from a formal grammar or anything like that, but that would be overkill. In this form it's quite easy to parse with simple scripts that just do line-based regexp matching, rather than proper parsing. And it's still very readable to humans, so I think it's a reasonable balance overall.
memory/replace/dmd/DMD.cpp
memory/replace/dmd/check_test_output.py
memory/replace/dmd/test-expected.dmd
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -201,20 +201,18 @@ Writer::Write(const char* aFmt, ...) con
   va_list ap;
   va_start(ap, aFmt);
   mWriterFun(mWriteState, aFmt, ap);
   va_end(ap);
 }
 
 #define W(...) aWriter.Write(__VA_ARGS__);
 
-#define WriteTitle(...)                                                       \
-  W("------------------------------------------------------------------\n");  \
-  W(__VA_ARGS__);                                                             \
-  W("------------------------------------------------------------------\n\n");
+#define WriteSeparator(...) \
+  W("#-----------------------------------------------------------------\n\n");
 
 MOZ_EXPORT void
 FpWrite(void* aWriteState, const char* aFmt, va_list aAp)
 {
   FILE* fp = static_cast<FILE*>(aWriteState);
   vfprintf(fp, aFmt, aAp);
 }
 
@@ -709,29 +707,29 @@ public:
     }
 
     MOZ_ASSERT(entry.mPc == aPc);
 
     uintptr_t entryPc = (uintptr_t)(entry.mPc);
     // Sometimes we get nothing useful.  Just print "???" for the entire entry
     // so that fix-linux-stack.pl doesn't complain about an empty filename.
     if (!entry.mFunction && !entry.mLibrary[0] && entry.mLOffset == 0) {
-      W("   ??? 0x%x\n", entryPc);
+      W("    ??? 0x%x\n", entryPc);
     } else {
       // Use "???" for unknown functions.
       const char* entryFunction = entry.mFunction ? entry.mFunction : "???";
       if (entry.mFileName) {
         // On Windows we can get the filename and line number at runtime.
-        W("   %s (%s:%lu) 0x%x\n",
+        W("    %s (%s:%lu) 0x%x\n",
           entryFunction, entry.mFileName, entry.mLineNo, entryPc);
       } else {
         // On Linux and Mac we cannot get the filename and line number at
         // runtime, so we print the offset in a form that fix-linux-stack.pl and
         // fix_macosx_stack.py can post-process.
-        W("   %s[%s +0x%X] 0x%x\n",
+        W("    %s[%s +0x%X] 0x%x\n",
           entryFunction, entry.mLibrary, entry.mLOffset, entryPc);
       }
     }
   }
 
   size_t SizeOfIncludingThis()
   {
     size_t n = MallocSizeOf(this);
@@ -1482,48 +1480,52 @@ typedef js::HashSet<Record, Record, Infa
 void
 Record::Print(const Writer& aWriter, LocationService* aLocService,
               uint32_t aM, uint32_t aN, const char* aStr, const char* astr,
               size_t aCategoryUsableSize, size_t aCumulativeUsableSize,
               size_t aTotalUsableSize) const
 {
   bool showTilde = mRecordSize.IsSampled();
 
-  W("%s: %s block%s in heap block record %s of %s\n",
-    aStr,
+  W("%s {\n", aStr);
+  W("  %s block%s in heap block record %s of %s\n",
     Show(mNumBlocks, gBuf1, kBufLen, showTilde), Plural(mNumBlocks),
     Show(aM, gBuf2, kBufLen),
     Show(aN, gBuf3, kBufLen));
 
-  W(" %s bytes (%s requested / %s slop)\n",
+  W("  %s bytes (%s requested / %s slop)\n",
     Show(mRecordSize.Usable(), gBuf1, kBufLen, showTilde),
     Show(mRecordSize.Req(),    gBuf2, kBufLen, showTilde),
     Show(mRecordSize.Slop(),   gBuf3, kBufLen, showTilde));
 
-  W(" %4.2f%% of the heap (%4.2f%% cumulative); "
-    " %4.2f%% of %s (%4.2f%% cumulative)\n",
+  W("  %4.2f%% of the heap (%4.2f%% cumulative)\n",
     Percent(mRecordSize.Usable(), aTotalUsableSize),
-    Percent(aCumulativeUsableSize, aTotalUsableSize),
+    Percent(aCumulativeUsableSize, aTotalUsableSize));
+
+  W("  %4.2f%% of %s (%4.2f%% cumulative)\n",
     Percent(mRecordSize.Usable(), aCategoryUsableSize),
     astr,
     Percent(aCumulativeUsableSize, aCategoryUsableSize));
 
-  W(" Allocated at\n");
+  W("  Allocated at {\n");
   mAllocStackTrace->Print(aWriter, aLocService);
+  W("  }\n");
 
   if (mReportStackTrace1) {
-    W("\n Reported at\n");
+    W("  Reported at {\n");
     mReportStackTrace1->Print(aWriter, aLocService);
+    W("  }\n");
   }
   if (mReportStackTrace2) {
-    W("\n Reported again at\n");
+    W("  Reported again at {\n");
     mReportStackTrace2->Print(aWriter, aLocService);
+    W("  }\n");
   }
 
-  W("\n");
+  W("}\n\n");
 }
 
 //---------------------------------------------------------------------------
 // Options (Part 2)
 //---------------------------------------------------------------------------
 
 // Given an |aOptionName| like "foo", succeed if |aArg| has the form "foo=blah"
 // (where "blah" is non-empty) and return the pointer to "blah".  |aArg| can
@@ -1818,20 +1820,20 @@ PrintSortedRecords(const Writer& aWriter
   for (RecordTable::Range r = aRecordTable.all();
        !r.empty();
        r.popFront()) {
     recordArray.infallibleAppend(&r.front());
   }
   qsort(recordArray.begin(), recordArray.length(), sizeof(recordArray[0]),
         Record::QsortCmp);
 
-  WriteTitle("%s heap block records\n", aStr);
+  WriteSeparator();
 
   if (recordArray.length() == 0) {
-    W("(none)\n\n");
+    W("# no %s heap blocks\n\n", astr);
     return;
   }
 
   StatusMsg("  printing %s heap block record array...\n", astr);
   size_t cumulativeUsableSize = 0;
 
   // Limit the number of records printed, because fix-linux-stack.pl is too
   // damn slow.  Note that we don't break out of this loop because we need to
@@ -1840,17 +1842,17 @@ PrintSortedRecords(const Writer& aWriter
   uint32_t maxRecords = gOptions->MaxRecords();
   for (uint32_t i = 0; i < numRecords; i++) {
     const Record* r = recordArray[i];
     cumulativeUsableSize += r->GetRecordSize().Usable();
     if (i < maxRecords) {
       r->Print(aWriter, aLocService, i+1, numRecords, aStr, astr,
                aCategoryUsableSize, cumulativeUsableSize, aTotalUsableSize);
     } else if (i == maxRecords) {
-      W("%s: stopping after %s heap block records\n\n", aStr,
+      W("# %s: stopping after %s heap block records\n\n", aStr,
         Show(maxRecords, gBuf1, kBufLen));
     }
   }
   MOZ_ASSERT(aCategoryUsableSize == cumulativeUsableSize);
 }
 
 // Note that, unlike most SizeOf* functions, this function does not take a
 // |mozilla::MallocSizeOf| argument.  That's because those arguments are
@@ -2002,20 +2004,21 @@ Dump(Writer aWriter)
 
     anyBlocksSampled = anyBlocksSampled || b.IsSampled();
   }
   size_t totalUsableSize =
     unreportedUsableSize + onceReportedUsableSize + twiceReportedUsableSize;
   size_t totalNumBlocks =
     unreportedNumBlocks + onceReportedNumBlocks + twiceReportedNumBlocks;
 
-  WriteTitle("Invocation\n");
-  W("$DMD = '%s'\n", gOptions->DMDEnvVar());
-  W("Sample-below size = %lld\n\n",
-    (long long)(gOptions->SampleBelowSize()));
+  WriteSeparator();
+  W("Invocation {\n");
+  W("  $DMD = '%s'\n", gOptions->DMDEnvVar());
+  W("  Sample-below size = %lld\n", (long long)(gOptions->SampleBelowSize()));
+  W("}\n\n");
 
   // Allocate this on the heap instead of the stack because it's fairly large.
   LocationService* locService = InfallibleAllocPolicy::new_<LocationService>();
 
   PrintSortedRecords(aWriter, locService,
                      "Twice-reported", "twice-reported",
                      twiceReportedRecordTable,
                      twiceReportedUsableSize, totalUsableSize);
@@ -2026,109 +2029,113 @@ Dump(Writer aWriter)
                      unreportedUsableSize, totalUsableSize);
 
   PrintSortedRecords(aWriter, locService,
                      "Once-reported", "once-reported",
                      onceReportedRecordTable,
                      onceReportedUsableSize, totalUsableSize);
 
   bool showTilde = anyBlocksSampled;
-  WriteTitle("Summary\n");
-
-  W("Total:          %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
+
+  WriteSeparator();
+  W("Summary {\n");
+
+  W("  Total:          %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
     Show(totalUsableSize, gBuf1, kBufLen, showTilde),
     100.0,
     Show(totalNumBlocks,  gBuf2, kBufLen, showTilde),
     100.0);
 
-  W("Unreported:     %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
+  W("  Unreported:     %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
     Show(unreportedUsableSize, gBuf1, kBufLen, showTilde),
     Percent(unreportedUsableSize, totalUsableSize),
     Show(unreportedNumBlocks, gBuf2, kBufLen, showTilde),
     Percent(unreportedNumBlocks, totalNumBlocks));
 
-  W("Once-reported:  %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
+  W("  Once-reported:  %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
     Show(onceReportedUsableSize, gBuf1, kBufLen, showTilde),
     Percent(onceReportedUsableSize, totalUsableSize),
     Show(onceReportedNumBlocks, gBuf2, kBufLen, showTilde),
     Percent(onceReportedNumBlocks, totalNumBlocks));
 
-  W("Twice-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
+  W("  Twice-reported: %12s bytes (%6.2f%%) in %7s blocks (%6.2f%%)\n",
     Show(twiceReportedUsableSize, gBuf1, kBufLen, showTilde),
     Percent(twiceReportedUsableSize, totalUsableSize),
     Show(twiceReportedNumBlocks, gBuf2, kBufLen, showTilde),
     Percent(twiceReportedNumBlocks, totalNumBlocks));
 
-  W("\n");
+  W("}\n\n");
 
   // Stats are non-deterministic, so don't show them in test mode.
   if (!gOptions->IsTestMode()) {
     Sizes sizes;
     SizeOfInternal(&sizes);
 
-    WriteTitle("Execution measurements\n");
-
-    W("Data structures that persist after Dump() ends:\n");
-
-    W("  Used stack traces:    %10s bytes\n",
+    WriteSeparator();
+    W("Execution measurements {\n");
+
+    W("  Data structures that persist after Dump() ends:\n");
+
+    W("    Used stack traces:    %10s bytes\n",
       Show(sizes.mStackTracesUsed, gBuf1, kBufLen));
 
-    W("  Unused stack traces:  %10s bytes\n",
+    W("    Unused stack traces:  %10s bytes\n",
       Show(sizes.mStackTracesUnused, gBuf1, kBufLen));
 
-    W("  Stack trace table:    %10s bytes (%s entries, %s used)\n",
+    W("    Stack trace table:    %10s bytes (%s entries, %s used)\n",
       Show(sizes.mStackTraceTable,       gBuf1, kBufLen),
       Show(gStackTraceTable->capacity(), gBuf2, kBufLen),
       Show(gStackTraceTable->count(),    gBuf3, kBufLen));
 
-    W("  Block table:          %10s bytes (%s entries, %s used)\n",
+    W("    Block table:          %10s bytes (%s entries, %s used)\n",
       Show(sizes.mBlockTable,       gBuf1, kBufLen),
       Show(gBlockTable->capacity(), gBuf2, kBufLen),
       Show(gBlockTable->count(),    gBuf3, kBufLen));
 
-    W("\nData structures that are destroyed after Dump() ends:\n");
+    W("\n  Data structures that are destroyed after Dump() ends:\n");
 
     size_t unreportedSize =
       unreportedRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("  Unreported table:     %10s bytes (%s entries, %s used)\n",
+    W("    Unreported table:     %10s bytes (%s entries, %s used)\n",
       Show(unreportedSize,                   gBuf1, kBufLen),
       Show(unreportedRecordTable.capacity(), gBuf2, kBufLen),
       Show(unreportedRecordTable.count(),    gBuf3, kBufLen));
 
     size_t onceReportedSize =
       onceReportedRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("  Once-reported table:  %10s bytes (%s entries, %s used)\n",
+    W("    Once-reported table:  %10s bytes (%s entries, %s used)\n",
       Show(onceReportedSize,                   gBuf1, kBufLen),
       Show(onceReportedRecordTable.capacity(), gBuf2, kBufLen),
       Show(onceReportedRecordTable.count(),    gBuf3, kBufLen));
 
     size_t twiceReportedSize =
       twiceReportedRecordTable.sizeOfIncludingThis(MallocSizeOf);
-    W("  Twice-reported table: %10s bytes (%s entries, %s used)\n",
+    W("    Twice-reported table: %10s bytes (%s entries, %s used)\n",
       Show(twiceReportedSize,                   gBuf1, kBufLen),
       Show(twiceReportedRecordTable.capacity(), gBuf2, kBufLen),
       Show(twiceReportedRecordTable.count(),    gBuf3, kBufLen));
 
-    W("  Location service:     %10s bytes\n",
+    W("    Location service:     %10s bytes\n",
       Show(locService->SizeOfIncludingThis(), gBuf1, kBufLen));
 
-    W("\nCounts:\n");
+    W("\n  Counts:\n");
 
     size_t hits   = locService->NumCacheHits();
     size_t misses = locService->NumCacheMisses();
     size_t requests = hits + misses;
-    W("  Location service:    %10s requests\n",
+    W("    Location service:    %10s requests\n",
       Show(requests, gBuf1, kBufLen));
 
     size_t count    = locService->CacheCount();
     size_t capacity = locService->CacheCapacity();
-    W("  Location service cache:  %4.1f%% hit rate, %.1f%% occupancy at end\n",
+    W("    Location service cache:  "
+      "%4.1f%% hit rate, %.1f%% occupancy at end\n",
       Percent(hits, requests), Percent(count, capacity));
 
-    W("\n");
+    W("}\n\n");
   }
 
   InfallibleAllocPolicy::delete_(locService);
 
   ClearReportsInternal(); // Use internal version, we already have the lock.
 
   StatusMsg("}\n");
 }
--- a/memory/replace/dmd/check_test_output.py
+++ b/memory/replace/dmd/check_test_output.py
@@ -70,32 +70,32 @@ def main():
     print("filtering output to", filtered_name)
 
     with open(fixed_name, "r") as fin, \
          open(filtered_name, "w") as fout:
 
         test_frame_re = re.compile(r".*(DMD.cpp)")
 
         for line in fin:
-            if re.match(r" (Allocated at|Reported( again)? at)", line):
+            if re.match(r"  (Allocated at {|Reported( again)? at {)", line):
                 # It's a heap block record.
                 print(line, end='', file=fout)
 
                 # Filter the stack trace -- print a single line if we see one
                 # or more frames involving DMD.cpp.
                 seen_DMD_frame = False
                 for frame in fin:
-                    if re.match(r"   ", frame):
+                    if re.match(r"    ", frame):
                         m = test_frame_re.match(frame)
                         if m:
                             seen_DMD_frame = True
                     else:
                         # We're past the stack trace.
                         if seen_DMD_frame:
-                            print("   ... DMD.cpp", file=fout)
+                            print("    ... DMD.cpp", file=fout)
                         print(frame, end='', file=fout)
                         break
 
             else:
                 # A line that needs no special handling.  Copy it through.
                 print(line, end='', file=fout)
 
     # Compare with expected output
--- a/memory/replace/dmd/test-expected.dmd
+++ b/memory/replace/dmd/test-expected.dmd
@@ -1,407 +1,519 @@
-------------------------------------------------------------------
-Invocation
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-$DMD = '--mode=test'
-Sample-below size = 1
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
 
-------------------------------------------------------------------
-Twice-reported heap block records
-------------------------------------------------------------------
+# no twice-reported heap blocks
 
-(none)
+#-----------------------------------------------------------------
+
+# no unreported heap blocks
 
-------------------------------------------------------------------
-Unreported heap block records
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-(none)
+# no once-reported heap blocks
+
+#-----------------------------------------------------------------
 
-------------------------------------------------------------------
-Once-reported heap block records
-------------------------------------------------------------------
+Summary {
+  Total:                     0 bytes (100.00%) in       0 blocks (100.00%)
+  Unreported:                0 bytes (  0.00%) in       0 blocks (  0.00%)
+  Once-reported:             0 bytes (  0.00%) in       0 blocks (  0.00%)
+  Twice-reported:            0 bytes (  0.00%) in       0 blocks (  0.00%)
+}
 
-(none)
+#-----------------------------------------------------------------
 
-------------------------------------------------------------------
-Summary
-------------------------------------------------------------------
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
 
-Total:                     0 bytes (100.00%) in       0 blocks (100.00%)
-Unreported:                0 bytes (  0.00%) in       0 blocks (  0.00%)
-Once-reported:             0 bytes (  0.00%) in       0 blocks (  0.00%)
-Twice-reported:            0 bytes (  0.00%) in       0 blocks (  0.00%)
-
-------------------------------------------------------------------
-Invocation
-------------------------------------------------------------------
-
-$DMD = '--mode=test'
-Sample-below size = 1
-
-------------------------------------------------------------------
-Twice-reported heap block records
-------------------------------------------------------------------
+Twice-reported {
+  1 block in heap block record 1 of 4
+  80 bytes (79 requested / 1 slop)
+  0.66% of the heap (0.66% cumulative)
+  29.41% of twice-reported (29.41% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
 
-Twice-reported: 1 block in heap block record 1 of 4
- 80 bytes (79 requested / 1 slop)
- 0.66% of the heap (0.66% cumulative);  29.41% of twice-reported (29.41% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
-
- Reported again at
-   ... DMD.cpp
+Twice-reported {
+  1 block in heap block record 2 of 4
+  80 bytes (78 requested / 2 slop)
+  0.66% of the heap (1.32% cumulative)
+  29.41% of twice-reported (58.82% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
 
-Twice-reported: 1 block in heap block record 2 of 4
- 80 bytes (78 requested / 2 slop)
- 0.66% of the heap (1.32% cumulative);  29.41% of twice-reported (58.82% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
-
- Reported again at
-   ... DMD.cpp
+Twice-reported {
+  1 block in heap block record 3 of 4
+  80 bytes (77 requested / 3 slop)
+  0.66% of the heap (1.99% cumulative)
+  29.41% of twice-reported (88.24% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
 
-Twice-reported: 1 block in heap block record 3 of 4
- 80 bytes (77 requested / 3 slop)
- 0.66% of the heap (1.99% cumulative);  29.41% of twice-reported (88.24% cumulative)
- Allocated at
-   ... DMD.cpp
+Twice-reported {
+  1 block in heap block record 4 of 4
+  32 bytes (30 requested / 2 slop)
+  0.26% of the heap (2.25% cumulative)
+  11.76% of twice-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
 
- Reported at
-   ... DMD.cpp
-
- Reported again at
-   ... DMD.cpp
+#-----------------------------------------------------------------
 
-Twice-reported: 1 block in heap block record 4 of 4
- 32 bytes (30 requested / 2 slop)
- 0.26% of the heap (2.25% cumulative);  11.76% of twice-reported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  9 blocks in heap block record 1 of 3
+  1,008 bytes (900 requested / 108 slop)
+  8.34% of the heap (8.34% cumulative)
+  81.82% of unreported (81.82% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
- Reported at
-   ... DMD.cpp
-
- Reported again at
-   ... DMD.cpp
-
-------------------------------------------------------------------
-Unreported heap block records
-------------------------------------------------------------------
+Unreported {
+  2 blocks in heap block record 2 of 3
+  112 bytes (112 requested / 0 slop)
+  0.93% of the heap (9.27% cumulative)
+  9.09% of unreported (90.91% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: 9 blocks in heap block record 1 of 3
- 1,008 bytes (900 requested / 108 slop)
- 8.34% of the heap (8.34% cumulative);  81.82% of unreported (81.82% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  2 blocks in heap block record 3 of 3
+  112 bytes (112 requested / 0 slop)
+  0.93% of the heap (10.19% cumulative)
+  9.09% of unreported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
 
-Unreported: 2 blocks in heap block record 2 of 3
- 112 bytes (112 requested / 0 slop)
- 0.93% of the heap (9.27% cumulative);  9.09% of unreported (90.91% cumulative)
- Allocated at
-   ... DMD.cpp
-
-Unreported: 2 blocks in heap block record 3 of 3
- 112 bytes (112 requested / 0 slop)
- 0.93% of the heap (10.19% cumulative);  9.09% of unreported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
-
-------------------------------------------------------------------
-Once-reported heap block records
-------------------------------------------------------------------
+Once-reported {
+  1 block in heap block record 1 of 11
+  8,192 bytes (4,097 requested / 4,095 slop)
+  67.77% of the heap (67.77% cumulative)
+  77.40% of once-reported (77.40% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 1 of 11
- 8,192 bytes (4,097 requested / 4,095 slop)
- 67.77% of the heap (67.77% cumulative);  77.40% of once-reported (77.40% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 2 of 11
+  1,024 bytes (1,023 requested / 1 slop)
+  8.47% of the heap (76.24% cumulative)
+  9.67% of once-reported (87.07% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 2 of 11
- 1,024 bytes (1,023 requested / 1 slop)
- 8.47% of the heap (76.24% cumulative);  9.67% of once-reported (87.07% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 3 of 11
+  512 bytes (512 requested / 0 slop)
+  4.24% of the heap (80.48% cumulative)
+  4.84% of once-reported (91.91% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 3 of 11
- 512 bytes (512 requested / 0 slop)
- 4.24% of the heap (80.48% cumulative);  4.84% of once-reported (91.91% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  2 blocks in heap block record 4 of 11
+  240 bytes (240 requested / 0 slop)
+  1.99% of the heap (82.46% cumulative)
+  2.27% of once-reported (94.18% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 2 blocks in heap block record 4 of 11
- 240 bytes (240 requested / 0 slop)
- 1.99% of the heap (82.46% cumulative);  2.27% of once-reported (94.18% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  2 blocks in heap block record 5 of 11
+  240 bytes (240 requested / 0 slop)
+  1.99% of the heap (84.45% cumulative)
+  2.27% of once-reported (96.45% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 2 blocks in heap block record 5 of 11
- 240 bytes (240 requested / 0 slop)
- 1.99% of the heap (84.45% cumulative);  2.27% of once-reported (96.45% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
-
-Once-reported: 1 block in heap block record 6 of 11
- 96 bytes (96 requested / 0 slop)
- 0.79% of the heap (85.24% cumulative);  0.91% of once-reported (97.35% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 6 of 11
+  96 bytes (96 requested / 0 slop)
+  0.79% of the heap (85.24% cumulative)
+  0.91% of once-reported (97.35% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 7 of 11
- 96 bytes (96 requested / 0 slop)
- 0.79% of the heap (86.04% cumulative);  0.91% of once-reported (98.26% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 7 of 11
+  96 bytes (96 requested / 0 slop)
+  0.79% of the heap (86.04% cumulative)
+  0.91% of once-reported (98.26% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 8 of 11
- 80 bytes (80 requested / 0 slop)
- 0.66% of the heap (86.70% cumulative);  0.76% of once-reported (99.02% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 8 of 11
+  80 bytes (80 requested / 0 slop)
+  0.66% of the heap (86.70% cumulative)
+  0.76% of once-reported (99.02% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 9 of 11
- 80 bytes (80 requested / 0 slop)
- 0.66% of the heap (87.36% cumulative);  0.76% of once-reported (99.77% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 9 of 11
+  80 bytes (80 requested / 0 slop)
+  0.66% of the heap (87.36% cumulative)
+  0.76% of once-reported (99.77% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 10 of 11
- 16 bytes (10 requested / 6 slop)
- 0.13% of the heap (87.49% cumulative);  0.15% of once-reported (99.92% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 10 of 11
+  16 bytes (10 requested / 6 slop)
+  0.13% of the heap (87.49% cumulative)
+  0.15% of once-reported (99.92% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Once-reported: 1 block in heap block record 11 of 11
- 8 bytes (0 requested / 8 slop)
- 0.07% of the heap (87.56% cumulative);  0.08% of once-reported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 11 of 11
+  8 bytes (0 requested / 8 slop)
+  0.07% of the heap (87.56% cumulative)
+  0.08% of once-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
- Reported at
-   ... DMD.cpp
-
-------------------------------------------------------------------
-Summary
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-Total:                12,088 bytes (100.00%) in      30 blocks (100.00%)
-Unreported:            1,232 bytes ( 10.19%) in      13 blocks ( 43.33%)
-Once-reported:        10,584 bytes ( 87.56%) in      13 blocks ( 43.33%)
-Twice-reported:          272 bytes (  2.25%) in       4 blocks ( 13.33%)
+Summary {
+  Total:                12,088 bytes (100.00%) in      30 blocks (100.00%)
+  Unreported:            1,232 bytes ( 10.19%) in      13 blocks ( 43.33%)
+  Once-reported:        10,584 bytes ( 87.56%) in      13 blocks ( 43.33%)
+  Twice-reported:          272 bytes (  2.25%) in       4 blocks ( 13.33%)
+}
 
-------------------------------------------------------------------
-Invocation
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-$DMD = '--mode=test'
-Sample-below size = 1
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 1
+}
+
+#-----------------------------------------------------------------
 
-------------------------------------------------------------------
-Twice-reported heap block records
-------------------------------------------------------------------
-
-Twice-reported: 1 block in heap block record 1 of 2
- 80 bytes (77 requested / 3 slop)
- 2.82% of the heap (2.82% cumulative);  90.91% of twice-reported (90.91% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
-
- Reported again at
-   ... DMD.cpp
+Twice-reported {
+  1 block in heap block record 1 of 2
+  80 bytes (77 requested / 3 slop)
+  2.82% of the heap (2.82% cumulative)
+  90.91% of twice-reported (90.91% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
 
-Twice-reported: 1 block in heap block record 2 of 2
- 8 bytes (0 requested / 8 slop)
- 0.28% of the heap (3.10% cumulative);  9.09% of twice-reported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Twice-reported {
+  1 block in heap block record 2 of 2
+  8 bytes (0 requested / 8 slop)
+  0.28% of the heap (3.10% cumulative)
+  9.09% of twice-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+  Reported again at {
+    ... DMD.cpp
+  }
+}
 
- Reported again at
-   ... DMD.cpp
+#-----------------------------------------------------------------
 
-------------------------------------------------------------------
-Unreported heap block records
-------------------------------------------------------------------
-
-Unreported: 9 blocks in heap block record 1 of 3
- 1,008 bytes (900 requested / 108 slop)
- 35.49% of the heap (35.49% cumulative);  48.84% of unreported (48.84% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  9 blocks in heap block record 1 of 3
+  1,008 bytes (900 requested / 108 slop)
+  35.49% of the heap (35.49% cumulative)
+  48.84% of unreported (48.84% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: 6 blocks in heap block record 2 of 3
- 528 bytes (528 requested / 0 slop)
- 18.59% of the heap (54.08% cumulative);  25.58% of unreported (74.42% cumulative)
- Allocated at
-   ... DMD.cpp
-
-Unreported: 6 blocks in heap block record 3 of 3
- 528 bytes (528 requested / 0 slop)
- 18.59% of the heap (72.68% cumulative);  25.58% of unreported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  6 blocks in heap block record 2 of 3
+  528 bytes (528 requested / 0 slop)
+  18.59% of the heap (54.08% cumulative)
+  25.58% of unreported (74.42% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-------------------------------------------------------------------
-Once-reported heap block records
-------------------------------------------------------------------
+Unreported {
+  6 blocks in heap block record 3 of 3
+  528 bytes (528 requested / 0 slop)
+  18.59% of the heap (72.68% cumulative)
+  25.58% of unreported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
+
+#-----------------------------------------------------------------
 
-Once-reported: 1 block in heap block record 1 of 4
- 512 bytes (512 requested / 0 slop)
- 18.03% of the heap (18.03% cumulative);  74.42% of once-reported (74.42% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
-
-Once-reported: 1 block in heap block record 2 of 4
- 80 bytes (79 requested / 1 slop)
- 2.82% of the heap (20.85% cumulative);  11.63% of once-reported (86.05% cumulative)
- Allocated at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 1 of 4
+  512 bytes (512 requested / 0 slop)
+  18.03% of the heap (18.03% cumulative)
+  74.42% of once-reported (74.42% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
- Reported at
-   ... DMD.cpp
-
-Once-reported: 1 block in heap block record 3 of 4
- 80 bytes (78 requested / 2 slop)
- 2.82% of the heap (23.66% cumulative);  11.63% of once-reported (97.67% cumulative)
- Allocated at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 2 of 4
+  80 bytes (79 requested / 1 slop)
+  2.82% of the heap (20.85% cumulative)
+  11.63% of once-reported (86.05% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
- Reported at
-   ... DMD.cpp
-
-Once-reported: 1 block in heap block record 4 of 4
- 16 bytes (10 requested / 6 slop)
- 0.56% of the heap (24.23% cumulative);  2.33% of once-reported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
-
- Reported at
-   ... DMD.cpp
+Once-reported {
+  1 block in heap block record 3 of 4
+  80 bytes (78 requested / 2 slop)
+  2.82% of the heap (23.66% cumulative)
+  11.63% of once-reported (97.67% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-------------------------------------------------------------------
-Summary
-------------------------------------------------------------------
+Once-reported {
+  1 block in heap block record 4 of 4
+  16 bytes (10 requested / 6 slop)
+  0.56% of the heap (24.23% cumulative)
+  2.33% of once-reported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+  Reported at {
+    ... DMD.cpp
+  }
+}
 
-Total:                 2,840 bytes (100.00%) in      27 blocks (100.00%)
-Unreported:            2,064 bytes ( 72.68%) in      21 blocks ( 77.78%)
-Once-reported:           688 bytes ( 24.23%) in       4 blocks ( 14.81%)
-Twice-reported:           88 bytes (  3.10%) in       2 blocks (  7.41%)
+#-----------------------------------------------------------------
 
-------------------------------------------------------------------
-Invocation
-------------------------------------------------------------------
+Summary {
+  Total:                 2,840 bytes (100.00%) in      27 blocks (100.00%)
+  Unreported:            2,064 bytes ( 72.68%) in      21 blocks ( 77.78%)
+  Once-reported:           688 bytes ( 24.23%) in       4 blocks ( 14.81%)
+  Twice-reported:           88 bytes (  3.10%) in       2 blocks (  7.41%)
+}
 
-$DMD = '--mode=test'
-Sample-below size = 128
+#-----------------------------------------------------------------
+
+Invocation {
+  $DMD = '--mode=test'
+  Sample-below size = 128
+}
+
+#-----------------------------------------------------------------
+
+# no twice-reported heap blocks
 
-------------------------------------------------------------------
-Twice-reported heap block records
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-(none)
-
-------------------------------------------------------------------
-Unreported heap block records
-------------------------------------------------------------------
+Unreported {
+  ~4 blocks in heap block record 1 of 7
+  ~512 bytes (~512 requested / ~0 slop)
+  35.96% of the heap (35.96% cumulative)
+  35.96% of unreported (35.96% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: ~4 blocks in heap block record 1 of 7
- ~512 bytes (~512 requested / ~0 slop)
- 35.96% of the heap (35.96% cumulative);  35.96% of unreported (35.96% cumulative)
- Allocated at
-   ... DMD.cpp
-
-Unreported: 1 block in heap block record 2 of 7
- 256 bytes (256 requested / 0 slop)
- 17.98% of the heap (53.93% cumulative);  17.98% of unreported (53.93% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  1 block in heap block record 2 of 7
+  256 bytes (256 requested / 0 slop)
+  17.98% of the heap (53.93% cumulative)
+  17.98% of unreported (53.93% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: 1 block in heap block record 3 of 7
- 144 bytes (144 requested / 0 slop)
- 10.11% of the heap (64.04% cumulative);  10.11% of unreported (64.04% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  1 block in heap block record 3 of 7
+  144 bytes (144 requested / 0 slop)
+  10.11% of the heap (64.04% cumulative)
+  10.11% of unreported (64.04% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: 1 block in heap block record 4 of 7
- 128 bytes (128 requested / 0 slop)
- 8.99% of the heap (73.03% cumulative);  8.99% of unreported (73.03% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  1 block in heap block record 4 of 7
+  128 bytes (128 requested / 0 slop)
+  8.99% of the heap (73.03% cumulative)
+  8.99% of unreported (73.03% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: ~1 block in heap block record 5 of 7
- ~128 bytes (~128 requested / ~0 slop)
- 8.99% of the heap (82.02% cumulative);  8.99% of unreported (82.02% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  ~1 block in heap block record 5 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (82.02% cumulative)
+  8.99% of unreported (82.02% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: ~1 block in heap block record 6 of 7
- ~128 bytes (~128 requested / ~0 slop)
- 8.99% of the heap (91.01% cumulative);  8.99% of unreported (91.01% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  ~1 block in heap block record 6 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (91.01% cumulative)
+  8.99% of unreported (91.01% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-Unreported: ~1 block in heap block record 7 of 7
- ~128 bytes (~128 requested / ~0 slop)
- 8.99% of the heap (100.00% cumulative);  8.99% of unreported (100.00% cumulative)
- Allocated at
-   ... DMD.cpp
+Unreported {
+  ~1 block in heap block record 7 of 7
+  ~128 bytes (~128 requested / ~0 slop)
+  8.99% of the heap (100.00% cumulative)
+  8.99% of unreported (100.00% cumulative)
+  Allocated at {
+    ... DMD.cpp
+  }
+}
 
-------------------------------------------------------------------
-Once-reported heap block records
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-(none)
+# no once-reported heap blocks
 
-------------------------------------------------------------------
-Summary
-------------------------------------------------------------------
+#-----------------------------------------------------------------
 
-Total:                ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
-Unreported:           ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
-Once-reported:            ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
-Twice-reported:           ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
+Summary {
+  Total:                ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
+  Unreported:           ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
+  Once-reported:            ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
+  Twice-reported:           ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
+}