Slightly refactor stack map stats printing.

Take advantage of the "operator" overload and move
the pretty print method so it can be better reused.

Test: m dump-oat
Change-Id: I46b169e044f2c4481bb2bc277601d61f873c9950
diff --git a/libartbase/base/stats-inl.h b/libartbase/base/stats-inl.h
new file mode 100644
index 0000000..5fa1372
--- /dev/null
+++ b/libartbase/base/stats-inl.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_STATS_INL_H_
+#define ART_LIBARTBASE_BASE_STATS_INL_H_
+
+#include <iomanip>
+#include <map>
+
+#include "base/stats.h"
+#include "base/indenter.h"
+
+namespace art {
+  void Stats::DumpSizes(VariableIndentationOutputStream& os, std::string_view name) const {
+    Dump(os, name, Value(), 1000.0, "KB");
+  }
+
+  void Stats::Dump(VariableIndentationOutputStream& os,
+                   std::string_view name,
+                   double total,
+                   double unit_size,
+                   const char* unit) const {
+    double percent = total > 0 ? 100.0 * Value() / total : 0;
+    os.Stream()
+        << std::setw(40 - os.GetIndentation()) << std::left << name << std::right << " "
+        << std::setw(8) << Count() << " "
+        << std::setw(12) << std::fixed << std::setprecision(3) << Value() / unit_size << unit
+        << std::setw(8) << std::fixed << std::setprecision(1) << percent << "%\n";
+
+    // Sort all children by largest value first, than by name.
+    std::map<std::pair<double, std::string_view>, const Stats&> sorted_children;
+    for (const auto& it : Children()) {
+      sorted_children.emplace(std::make_pair(-it.second.Value(), it.first), it.second);
+    }
+
+    // Add "other" row to represent any amount not account for by the children.
+    Stats other;
+    other.AddBytes(Value() - SumChildrenValues(), Count());
+    if (other.Value() != 0.0 && !Children().empty()) {
+      sorted_children.emplace(std::make_pair(-other.Value(), "(other)"), other);
+    }
+
+    // Print the data.
+    ScopedIndentation indent1(&os);
+    for (const auto& it : sorted_children) {
+      it.second.Dump(os, it.first.second, total, unit_size, unit);
+    }
+  }
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_STATS_INL_H_
diff --git a/libartbase/base/stats.h b/libartbase/base/stats.h
index 4dcbfe8..c568035 100644
--- a/libartbase/base/stats.h
+++ b/libartbase/base/stats.h
@@ -18,17 +18,20 @@
 #define ART_LIBARTBASE_BASE_STATS_H_
 
 #include <unordered_map>
+#include <string>
 
 #include "globals.h"
 
 namespace art {
 
+class VariableIndentationOutputStream;
+
 // Simple structure to record tree of statistical values.
 class Stats {
  public:
   double Value() const { return value_; }
   size_t Count() const { return count_; }
-  Stats* Child(const char* name) { return &children_[name]; }
+  Stats& operator[](const char* name) { return children_[name]; }
   const std::unordered_map<const char*, Stats>& Children() const { return children_; }
 
   void AddBytes(double bytes, size_t count = 1) { Add(bytes, count); }
@@ -44,12 +47,20 @@
     return sum;
   }
 
+  inline void DumpSizes(VariableIndentationOutputStream& os, std::string_view name) const;
+
  private:
   void Add(double value, size_t count = 1) {
     value_ += value;
     count_ += count;
   }
 
+  inline void Dump(VariableIndentationOutputStream& os,
+                   std::string_view name,
+                   double total,
+                   double unit_size,
+                   const char* unit) const;
+
   double value_ = 0.0;  // Commutative sum of the collected statistic in basic units.
   size_t count_ = 0;    // The number of samples for this node.
   std::unordered_map<const char*, Stats> children_;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 6c7f610..7e6a85e 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -39,7 +39,7 @@
 #include "base/indenter.h"
 #include "base/os.h"
 #include "base/safe_map.h"
-#include "base/stats.h"
+#include "base/stats-inl.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
@@ -696,7 +696,7 @@
       os << "OAT FILE STATS:\n";
       VariableIndentationOutputStream vios(&os);
       stats_.AddBytes(oat_file_.Size());
-      DumpStats(vios, "OatFile", stats_, stats_.Value());
+      stats_.DumpSizes(vios, "OatFile");
     }
 
     os << std::flush;
@@ -817,39 +817,6 @@
     return seen_stats_objects_.insert(address).second;  // Inserted new entry.
   }
 
-  void DumpStats(VariableIndentationOutputStream& os,
-                 const std::string& name,
-                 const Stats& stats,
-                 double total) {
-    if (std::fabs(stats.Value()) > 0 || !stats.Children().empty()) {
-      double percent = 100.0 * stats.Value() / total;
-      os.Stream()
-          << std::setw(40 - os.GetIndentation()) << std::left << name << std::right << " "
-          << std::setw(8) << stats.Count() << " "
-          << std::setw(12) << std::fixed << std::setprecision(3) << stats.Value() / KB << "KB "
-          << std::setw(8) << std::fixed << std::setprecision(1) << percent << "%\n";
-
-      // Sort all children by largest value first, than by name.
-      std::map<std::pair<double, std::string>, const Stats&> sorted_children;
-      for (const auto& it : stats.Children()) {
-        sorted_children.emplace(std::make_pair(-it.second.Value(), it.first), it.second);
-      }
-
-      // Add "other" row to represent any amount not account for by the children.
-      Stats other;
-      other.AddBytes(stats.Value() - stats.SumChildrenValues(), stats.Count());
-      if (std::fabs(other.Value()) > 0 && !stats.Children().empty()) {
-        sorted_children.emplace(std::make_pair(-other.Value(), "(other)"), other);
-      }
-
-      // Print the data.
-      ScopedIndentation indent1(&os);
-      for (const auto& it : sorted_children) {
-        DumpStats(os, it.first.second, it.second, total);
-      }
-    }
-  }
-
  private:
   void AddAllOffsets() {
     // We don't know the length of the code for each method, but we need to know where to stop
@@ -1215,7 +1182,7 @@
       uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
       const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
       if (AddStatsObject(method_header)) {
-        stats_.Child("QuickMethodHeader")->AddBytes(sizeof(*method_header));
+        stats_["QuickMethodHeader"].AddBytes(sizeof(*method_header));
       }
       if (options_.absolute_addresses_) {
         vios->Stream() << StringPrintf("%p ", method_header);
@@ -1289,7 +1256,7 @@
         uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
         uint64_t aligned_code_end = aligned_code_begin + code_size;
         if (AddStatsObject(code)) {
-          stats_.Child("Code")->AddBytes(code_size);
+          stats_["Code"].AddBytes(code_size);
         }
 
         if (options_.absolute_addresses_) {
@@ -1639,7 +1606,7 @@
       // The optimizing compiler outputs its CodeInfo data in the vmap table.
       StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_);
       if (AddStatsObject(oat_method.GetVmapTable())) {
-        helper.GetCodeInfo().CollectSizeStats(oat_method.GetVmapTable(), &stats_);
+        helper.GetCodeInfo().CollectSizeStats(oat_method.GetVmapTable(), stats_["CodeInfo"]);
       }
       const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
       size_t offset = 0;
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 5086f92..276a67c 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -21,7 +21,7 @@
 
 #include "art_method.h"
 #include "base/indenter.h"
-#include "base/stats.h"
+#include "base/stats-inl.h"
 #include "oat_quick_method_header.h"
 #include "scoped_thread_state_change-inl.h"
 
@@ -227,26 +227,26 @@
 }
 
 // Decode the CodeInfo while collecting size statistics.
-void CodeInfo::CollectSizeStats(const uint8_t* code_info_data, /*out*/ Stats* parent) {
-  Stats* codeinfo_stats = parent->Child("CodeInfo");
+void CodeInfo::CollectSizeStats(const uint8_t* code_info_data, /*out*/ Stats& stats) {
   BitMemoryReader reader(code_info_data);
   reader.ReadInterleavedVarints<kNumHeaders>();
-  codeinfo_stats->Child("Header")->AddBits(reader.NumberOfReadBits());
+  stats["Header"].AddBits(reader.NumberOfReadBits());
   size_t num_bits;
   CodeInfo code_info(code_info_data, &num_bits, [&](size_t i, auto* table, BitMemoryRegion region) {
     if (!code_info.IsBitTableDeduped(i)) {
-      Stats* table_stats = codeinfo_stats->Child(table->GetName());
-      table_stats->AddBits(region.size_in_bits());
+      Stats& table_stats = stats[table->GetName()];
+      table_stats.AddBits(region.size_in_bits());
+      table_stats["Header"].AddBits(region.size_in_bits() - table->DataBitSize());
       const char* const* column_names = table->GetColumnNames();
       for (size_t c = 0; c < table->NumColumns(); c++) {
         if (table->NumColumnBits(c) > 0) {
-          Stats* column_stats = table_stats->Child(column_names[c]);
-          column_stats->AddBits(table->NumRows() * table->NumColumnBits(c), table->NumRows());
+          Stats& column_stats = table_stats[column_names[c]];
+          column_stats.AddBits(table->NumRows() * table->NumColumnBits(c), table->NumRows());
         }
       }
     }
   });
-  codeinfo_stats->AddBytes(BitsToBytesRoundUp(num_bits));
+  stats.AddBytes(BitsToBytesRoundUp(num_bits));
 }
 
 void DexRegisterMap::Dump(VariableIndentationOutputStream* vios) const {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index e8e57aa..ce577c0 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -424,7 +424,7 @@
             InstructionSet instruction_set) const;
 
   // Accumulate code info size statistics into the given Stats tree.
-  static void CollectSizeStats(const uint8_t* code_info, /*out*/ Stats* parent);
+  static void CollectSizeStats(const uint8_t* code_info, /*out*/ Stats& parent);
 
   ALWAYS_INLINE static bool HasInlineInfo(const uint8_t* code_info_data) {
     return (*code_info_data & kHasInlineInfo) != 0;