Add instrumentation into SkArenaAlloc.

Add a parameter to the constructor that will have the dtor print out
stats for setting the initial parameters.

Clean up: Move some function so they are in the same order as .h

Change-Id: I19d87dcc9c3b8dcc3e1d4f2ff078b78bbc490d92
Reviewed-on: https://skia-review.googlesource.com/14600
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkArenaAlloc.cpp b/src/core/SkArenaAlloc.cpp
index eca3aa9..bfe3ff4 100644
--- a/src/core/SkArenaAlloc.cpp
+++ b/src/core/SkArenaAlloc.cpp
@@ -11,6 +11,64 @@
 
 static char* end_chain(char*) { return nullptr; }
 
+SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize, Tracking tracking)
+    : fDtorCursor {block}
+    , fCursor     {block}
+    , fEnd        {block + SkTo<uint32_t>(size)}
+    , fFirstBlock {block}
+    , fFirstSize  {SkTo<uint32_t>(size)}
+    , fExtraSize  {SkTo<uint32_t>(extraSize)}
+{
+    if (size < sizeof(Footer)) {
+        fEnd = fCursor = fDtorCursor = nullptr;
+    }
+
+    if (tracking == kTrack) {
+        fTotalSlop = 0;
+    }
+
+    if (fCursor != nullptr) {
+        this->installFooter(end_chain, 0);
+        if (fTotalSlop >= 0) {
+            fTotalAlloc += fFirstSize;
+        }
+    }
+}
+
+SkArenaAlloc::~SkArenaAlloc() {
+    if (fTotalSlop >= 0) {
+        int32_t lastSlop = fEnd - fCursor;
+        fTotalSlop += lastSlop;
+        SkDebugf("SkArenaAlloc initial: %p %u %u total alloc: %u total slop: %d last slop: %d\n",
+            fFirstBlock, fFirstSize, fExtraSize, fTotalAlloc, fTotalSlop, lastSlop);
+    }
+    RunDtorsOnBlock(fDtorCursor);
+}
+
+void SkArenaAlloc::reset() {
+    this->~SkArenaAlloc();
+    new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize,
+                            fTotalSlop < 0 ? kDontTrack : kTrack};
+}
+
+void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
+    SkASSERT(padding < 64);
+    int64_t actionInt = (int64_t)(intptr_t)action;
+
+    // The top 14 bits should be either all 0s or all 1s. Check this.
+    SkASSERT((actionInt << 6) >> 6 == actionInt);
+    Footer encodedFooter = (actionInt << 6) | padding;
+    memmove(fCursor, &encodedFooter, sizeof(Footer));
+    fCursor += sizeof(Footer);
+    fDtorCursor = fCursor;
+}
+
+void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
+    memmove(fCursor, &ptr, sizeof(char*));
+    fCursor += sizeof(char*);
+    this->installFooter(action, padding);
+}
+
 char* SkArenaAlloc::SkipPod(char* footerEnd) {
     char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
     int32_t skip;
@@ -39,50 +97,6 @@
     return nullptr;
 }
 
-SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
-    : fDtorCursor {block}
-    , fCursor     {block}
-    , fEnd        {block + SkTo<uint32_t>(size)}
-    , fFirstBlock {block}
-    , fFirstSize  {SkTo<uint32_t>(size)}
-    , fExtraSize  {SkTo<uint32_t>(extraSize)}
-{
-    if (size < sizeof(Footer)) {
-        fEnd = fCursor = fDtorCursor = nullptr;
-    }
-
-    if (fCursor != nullptr) {
-        this->installFooter(end_chain, 0);
-    }
-}
-
-SkArenaAlloc::~SkArenaAlloc() {
-    RunDtorsOnBlock(fDtorCursor);
-}
-
-void SkArenaAlloc::reset() {
-    this->~SkArenaAlloc();
-    new (this) SkArenaAlloc{fFirstBlock, fFirstSize, fExtraSize};
-}
-
-void SkArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
-    SkASSERT(padding < 64);
-    int64_t actionInt = (int64_t)(intptr_t)action;
-
-    // The top 14 bits should be either all 0s or all 1s. Check this.
-    SkASSERT((actionInt << 6) >> 6 == actionInt);
-    Footer encodedFooter = (actionInt << 6) | padding;
-    memmove(fCursor, &encodedFooter, sizeof(Footer));
-    fCursor += sizeof(Footer);
-    fDtorCursor = fCursor;
-}
-
-void SkArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
-    memmove(fCursor, &ptr, sizeof(char*));
-    fCursor += sizeof(char*);
-    this->installFooter(action, padding);
-}
-
 void SkArenaAlloc::installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding) {
     memmove(fCursor, &value, sizeof(uint32_t));
     fCursor += sizeof(uint32_t);
@@ -113,6 +127,11 @@
 
     char* newBlock = new char[allocationSize];
 
+    if (fTotalSlop >= 0) {
+        fTotalAlloc += allocationSize;
+        fTotalSlop += fEnd - fCursor;
+    }
+
     auto previousDtor = fDtorCursor;
     fCursor = newBlock;
     fDtorCursor = newBlock;
diff --git a/src/core/SkArenaAlloc.h b/src/core/SkArenaAlloc.h
index 488adfc..e838579 100644
--- a/src/core/SkArenaAlloc.h
+++ b/src/core/SkArenaAlloc.h
@@ -53,21 +53,30 @@
 // For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There is an
 // addition overhead when switching from POD data to non-POD data of typically 8 bytes.
 //
+// You can track memory use by adding SkArenaAlloc::kTrack as the last parameter to any constructor.
+//
+//   char storage[someNumber];
+//   SkArenaAlloc alloc{storage, SkArenaAlloc::kTrack};
+//
+// This will print out a line for every destructor or reset call that has the total memory
+// allocated, the total slop (the unused portion of a block), and the slop of the last block.
+//
 // If additional blocks are needed they are increased exponentially. This strategy bounds the
 // recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using
 // the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48
 // there are 71 allocations.
 class SkArenaAlloc {
 public:
-    SkArenaAlloc(char* block, size_t size, size_t extraSize);
+    enum Tracking {kDontTrack, kTrack};
+    SkArenaAlloc(char* block, size_t size, size_t, Tracking tracking = kDontTrack);
 
     template <size_t kSize>
-    SkArenaAlloc(char (&block)[kSize], size_t extraSize = kSize)
-        : SkArenaAlloc(block, kSize, extraSize)
+    SkArenaAlloc(char (&block)[kSize], size_t extraSize = kSize, Tracking tracking = kDontTrack)
+        : SkArenaAlloc(block, kSize, extraSize, tracking)
     {}
 
-    SkArenaAlloc(size_t extraSize)
-        : SkArenaAlloc(nullptr, 0, extraSize)
+    SkArenaAlloc(size_t extraSize, Tracking tracking = kDontTrack)
+        : SkArenaAlloc(nullptr, 0, extraSize, tracking)
     {}
 
     ~SkArenaAlloc();
@@ -197,6 +206,11 @@
     char* const    fFirstBlock;
     const uint32_t fFirstSize;
     const uint32_t fExtraSize;
+
+    // Track some useful stats. Track stats if fTotalSlop is >= 0;
+    uint32_t       fTotalAlloc { 0};
+    int32_t        fTotalSlop  {-1};
+
     // Use the Fibonacci sequence as the growth factor for block size. The size of the block
     // allocated is fFib0 * fExtraSize. Using 2 ^ n * fExtraSize had too much slop for Android.
     uint32_t       fFib0 {1}, fFib1 {1};