ART: Don't nest indenters in oatdump.

Reduces the time taken by the oatdump_test by ~12s (15%)
on host and ~55s (9%) on N5.

Change-Id: I99bb16ff5f3640389815f1fe54379ca64eac071b
diff --git a/runtime/indenter.h b/runtime/indenter.h
index 38b398d..78b18f6 100644
--- a/runtime/indenter.h
+++ b/runtime/indenter.h
@@ -19,10 +19,13 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include <ostream>
 #include <streambuf>
 
-const char kIndentChar =' ';
-const size_t kIndentBy1Count = 2;
+namespace art {
+
+constexpr char kIndentChar =' ';
+constexpr size_t kIndentBy1Count = 2;
 
 class Indenter : public std::streambuf {
  public:
@@ -99,9 +102,60 @@
   const char text_[8];
 
   // Number of times text is output.
-  const size_t count_;
+  size_t count_;
+
+  friend class VariableIndentationOutputStream;
 
   DISALLOW_COPY_AND_ASSIGN(Indenter);
 };
 
+class VariableIndentationOutputStream {
+ public:
+  explicit VariableIndentationOutputStream(std::ostream* os, char text = kIndentChar)
+      : indenter_(os->rdbuf(), text, 0u),
+        indented_os_(&indenter_) {
+  }
+
+  std::ostream& Stream() {
+    return indented_os_;
+  }
+
+  void IncreaseIndentation(size_t adjustment) {
+    indenter_.count_ += adjustment;
+  }
+
+  void DecreaseIndentation(size_t adjustment) {
+    DCHECK_GE(indenter_.count_, adjustment);
+    indenter_.count_ -= adjustment;
+  }
+
+ private:
+  Indenter indenter_;
+  std::ostream indented_os_;
+
+  DISALLOW_COPY_AND_ASSIGN(VariableIndentationOutputStream);
+};
+
+class ScopedIndentation {
+ public:
+  explicit ScopedIndentation(VariableIndentationOutputStream* vios,
+                             size_t adjustment = kIndentBy1Count)
+      : vios_(vios),
+        adjustment_(adjustment) {
+    vios_->IncreaseIndentation(adjustment_);
+  }
+
+  ~ScopedIndentation() {
+    vios_->DecreaseIndentation(adjustment_);
+  }
+
+ private:
+  VariableIndentationOutputStream* const vios_;
+  const size_t adjustment_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedIndentation);
+};
+
+}  // namespace art
+
 #endif  // ART_RUNTIME_INDENTER_H_
diff --git a/runtime/indenter_test.cc b/runtime/indenter_test.cc
index 1919e3d..1a26d7b 100644
--- a/runtime/indenter_test.cc
+++ b/runtime/indenter_test.cc
@@ -17,6 +17,8 @@
 #include "gtest/gtest.h"
 #include "indenter.h"
 
+namespace art {
+
 TEST(IndenterTest, MultiLineTest) {
   std::ostringstream output;
   Indenter indent_filter(output.rdbuf(), '\t', 2);
@@ -33,3 +35,5 @@
   input << "\n";
   EXPECT_EQ(output.str(), "\t\thello\n\t\thello again\n");
 }
+
+}  // namespace art
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 741cd90..962132b 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -95,40 +95,37 @@
                                 DexRegisterLocation location,
                                 const std::string& prefix = "v",
                                 const std::string& suffix = "") {
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indented_os(&indent_filter);
-  indented_os << prefix << dex_register_num << ": "
-              << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
-              << " (" << location.GetValue() << ")" << suffix << '\n';
+  os << prefix << dex_register_num << ": "
+     << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
+     << " (" << location.GetValue() << ")" << suffix << '\n';
 }
 
-void CodeInfo::Dump(std::ostream& os,
+void CodeInfo::Dump(VariableIndentationOutputStream* vios,
                     uint32_t code_offset,
                     uint16_t number_of_dex_registers,
                     bool dump_stack_maps) const {
   StackMapEncoding encoding = ExtractEncoding();
   uint32_t code_info_size = GetOverallSize();
   size_t number_of_stack_maps = GetNumberOfStackMaps();
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indented_os(&indent_filter);
-  indented_os << "Optimized CodeInfo (size=" << code_info_size
-              << ", number_of_dex_registers=" << number_of_dex_registers
-              << ", number_of_stack_maps=" << number_of_stack_maps
-              << ", has_inline_info=" << encoding.HasInlineInfo()
-              << ", number_of_bytes_for_inline_info=" << encoding.NumberOfBytesForInlineInfo()
-              << ", number_of_bytes_for_dex_register_map="
-                  << encoding.NumberOfBytesForDexRegisterMap()
-              << ", number_of_bytes_for_dex_pc=" << encoding.NumberOfBytesForDexPc()
-              << ", number_of_bytes_for_native_pc=" << encoding.NumberOfBytesForNativePc()
-              << ", number_of_bytes_for_register_mask=" << encoding.NumberOfBytesForRegisterMask()
-              << ")\n";
+  vios->Stream()
+      << "Optimized CodeInfo (size=" << code_info_size
+      << ", number_of_dex_registers=" << number_of_dex_registers
+      << ", number_of_stack_maps=" << number_of_stack_maps
+      << ", has_inline_info=" << encoding.HasInlineInfo()
+      << ", number_of_bytes_for_inline_info=" << encoding.NumberOfBytesForInlineInfo()
+      << ", number_of_bytes_for_dex_register_map=" << encoding.NumberOfBytesForDexRegisterMap()
+      << ", number_of_bytes_for_dex_pc=" << encoding.NumberOfBytesForDexPc()
+      << ", number_of_bytes_for_native_pc=" << encoding.NumberOfBytesForNativePc()
+      << ", number_of_bytes_for_register_mask=" << encoding.NumberOfBytesForRegisterMask()
+      << ")\n";
+  ScopedIndentation indent1(vios);
   // Display the Dex register location catalog.
-  GetDexRegisterLocationCatalog(encoding).Dump(indented_os, *this);
+  GetDexRegisterLocationCatalog(encoding).Dump(vios, *this);
   // Display stack maps along with (live) Dex register maps.
   if (dump_stack_maps) {
     for (size_t i = 0; i < number_of_stack_maps; ++i) {
       StackMap stack_map = GetStackMapAt(i, encoding);
-      stack_map.Dump(indented_os,
+      stack_map.Dump(vios,
                      *this,
                      encoding,
                      code_offset,
@@ -140,30 +137,28 @@
   //       we need to know the number of dex registers for each inlined method.
 }
 
-void DexRegisterLocationCatalog::Dump(std::ostream& os, const CodeInfo& code_info) {
+void DexRegisterLocationCatalog::Dump(VariableIndentationOutputStream* vios,
+                                      const CodeInfo& code_info) {
   StackMapEncoding encoding = code_info.ExtractEncoding();
   size_t number_of_location_catalog_entries =
       code_info.GetNumberOfDexRegisterLocationCatalogEntries();
   size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(encoding);
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indented_os(&indent_filter);
-  indented_os
+  vios->Stream()
       << "DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries
       << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n";
   for (size_t i = 0; i < number_of_location_catalog_entries; ++i) {
     DexRegisterLocation location = GetDexRegisterLocation(i);
-    DumpRegisterMapping(indented_os, i, location, "entry ");
+    ScopedIndentation indent1(vios);
+    DumpRegisterMapping(vios->Stream(), i, location, "entry ");
   }
 }
 
-void DexRegisterMap::Dump(std::ostream& os,
+void DexRegisterMap::Dump(VariableIndentationOutputStream* vios,
                           const CodeInfo& code_info,
                           uint16_t number_of_dex_registers) const {
   StackMapEncoding encoding = code_info.ExtractEncoding();
   size_t number_of_location_catalog_entries =
       code_info.GetNumberOfDexRegisterLocationCatalogEntries();
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indented_os(&indent_filter);
   // TODO: Display the bit mask of live Dex registers.
   for (size_t j = 0; j < number_of_dex_registers; ++j) {
     if (IsDexRegisterLive(j)) {
@@ -173,70 +168,70 @@
                                                             number_of_dex_registers,
                                                             code_info,
                                                             encoding);
+      ScopedIndentation indent1(vios);
       DumpRegisterMapping(
-          indented_os, j, location, "v",
+          vios->Stream(), j, location, "v",
           "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]");
     }
   }
 }
 
-void StackMap::Dump(std::ostream& os,
+void StackMap::Dump(VariableIndentationOutputStream* vios,
                     const CodeInfo& code_info,
                     const StackMapEncoding& encoding,
                     uint32_t code_offset,
                     uint16_t number_of_dex_registers,
                     const std::string& header_suffix) const {
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indented_os(&indent_filter);
-  indented_os << "StackMap" << header_suffix
-              << std::hex
-              << " [native_pc=0x" << code_offset + GetNativePcOffset(encoding) << "]"
-              << " (dex_pc=0x" << GetDexPc(encoding)
-              << ", native_pc_offset=0x" << GetNativePcOffset(encoding)
-              << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(encoding)
-              << ", inline_info_offset=0x" << GetInlineDescriptorOffset(encoding)
-              << ", register_mask=0x" << GetRegisterMask(encoding)
-              << std::dec
-              << ", stack_mask=0b";
+  vios->Stream()
+      << "StackMap" << header_suffix
+      << std::hex
+      << " [native_pc=0x" << code_offset + GetNativePcOffset(encoding) << "]"
+      << " (dex_pc=0x" << GetDexPc(encoding)
+      << ", native_pc_offset=0x" << GetNativePcOffset(encoding)
+      << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(encoding)
+      << ", inline_info_offset=0x" << GetInlineDescriptorOffset(encoding)
+      << ", register_mask=0x" << GetRegisterMask(encoding)
+      << std::dec
+      << ", stack_mask=0b";
   MemoryRegion stack_mask = GetStackMask(encoding);
   for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) {
-    indented_os << stack_mask.LoadBit(e - i - 1);
+    vios->Stream() << stack_mask.LoadBit(e - i - 1);
   }
-  indented_os << ")\n";
+  vios->Stream() << ")\n";
   if (HasDexRegisterMap(encoding)) {
     DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
         *this, encoding, number_of_dex_registers);
-    dex_register_map.Dump(os, code_info, number_of_dex_registers);
+    dex_register_map.Dump(vios, code_info, number_of_dex_registers);
   }
   if (HasInlineInfo(encoding)) {
     InlineInfo inline_info = code_info.GetInlineInfoOf(*this, encoding);
     // We do not know the length of the dex register maps of inlined frames
     // at this level, so we just pass null to `InlineInfo::Dump` to tell
     // it not to look at these maps.
-    inline_info.Dump(os, code_info, nullptr);
+    inline_info.Dump(vios, code_info, nullptr);
   }
 }
 
-void InlineInfo::Dump(std::ostream& os,
+void InlineInfo::Dump(VariableIndentationOutputStream* vios,
                       const CodeInfo& code_info,
                       uint16_t number_of_dex_registers[]) const {
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indented_os(&indent_filter);
-  indented_os << "InlineInfo with depth " << static_cast<uint32_t>(GetDepth()) << "\n";
+  vios->Stream() << "InlineInfo with depth " << static_cast<uint32_t>(GetDepth()) << "\n";
 
   for (size_t i = 0; i < GetDepth(); ++i) {
-    indented_os << " At depth " << i
-                << std::hex
-                << " (dex_pc=0x" << GetDexPcAtDepth(i)
-                << std::dec
-                << ", method_index=" << GetMethodIndexAtDepth(i)
-                << ", invoke_type=" << static_cast<InvokeType>(GetInvokeTypeAtDepth(i))
-                << ")\n";
+    vios->Stream()
+        << " At depth " << i
+        << std::hex
+        << " (dex_pc=0x" << GetDexPcAtDepth(i)
+        << std::dec
+        << ", method_index=" << GetMethodIndexAtDepth(i)
+        << ", invoke_type=" << static_cast<InvokeType>(GetInvokeTypeAtDepth(i))
+        << ")\n";
     if (HasDexRegisterMapAtDepth(i) && (number_of_dex_registers != nullptr)) {
       StackMapEncoding encoding = code_info.ExtractEncoding();
       DexRegisterMap dex_register_map =
           code_info.GetDexRegisterMapAtDepth(i, *this, encoding, number_of_dex_registers[i]);
-      dex_register_map.Dump(indented_os, code_info, number_of_dex_registers[i]);
+      ScopedIndentation indent1(vios);
+      dex_register_map.Dump(vios, code_info, number_of_dex_registers[i]);
     }
   }
 }
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 4e42008..e8769f9 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -23,6 +23,8 @@
 
 namespace art {
 
+class VariableIndentationOutputStream;
+
 // Size of a frame slot, in bytes.  This constant is a signed value,
 // to please the compiler in arithmetic operations involving int32_t
 // (signed) values.
@@ -357,7 +359,7 @@
     return region_.size();
   }
 
-  void Dump(std::ostream& os, const CodeInfo& code_info);
+  void Dump(VariableIndentationOutputStream* vios, const CodeInfo& code_info);
 
   // Special (invalid) Dex register location catalog entry index meaning
   // that there is no location for a given Dex register (i.e., it is
@@ -610,7 +612,8 @@
     return region_.size();
   }
 
-  void Dump(std::ostream& o, const CodeInfo& code_info, uint16_t number_of_dex_registers) const;
+  void Dump(VariableIndentationOutputStream* vios,
+            const CodeInfo& code_info, uint16_t number_of_dex_registers) const;
 
  private:
   // Return the index in the Dex register map corresponding to the Dex
@@ -837,7 +840,7 @@
        && region_.size() == other.region_.size();
   }
 
-  void Dump(std::ostream& os,
+  void Dump(VariableIndentationOutputStream* vios,
             const CodeInfo& code_info,
             const StackMapEncoding& encoding,
             uint32_t code_offset,
@@ -931,7 +934,8 @@
     return kFixedEntrySize;
   }
 
-  void Dump(std::ostream& os, const CodeInfo& info, uint16_t* number_of_dex_registers) const;
+  void Dump(VariableIndentationOutputStream* vios,
+            const CodeInfo& info, uint16_t* number_of_dex_registers) const;
 
  private:
   // TODO: Instead of plain types such as "uint8_t", introduce
@@ -1120,7 +1124,7 @@
   // number of Dex virtual registers used in this method.  If
   // `dump_stack_maps` is true, also dump the stack maps and the
   // associated Dex register maps.
-  void Dump(std::ostream& os,
+  void Dump(VariableIndentationOutputStream* vios,
             uint32_t code_offset,
             uint16_t number_of_dex_registers,
             bool dump_stack_maps) const;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 09db7cd..11c3e65 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -349,27 +349,29 @@
   return result;
 }
 
-MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self, std::ostream& os, uint32_t dex_method_idx,
-                                         const DexFile* dex_file,
-                                         Handle<mirror::DexCache> dex_cache,
-                                         Handle<mirror::ClassLoader> class_loader,
-                                         const DexFile::ClassDef* class_def,
-                                         const DexFile::CodeItem* code_item,
-                                         ArtMethod* method,
-                                         uint32_t method_access_flags) {
+MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self,
+                                                    VariableIndentationOutputStream* vios,
+                                                    uint32_t dex_method_idx,
+                                                    const DexFile* dex_file,
+                                                    Handle<mirror::DexCache> dex_cache,
+                                                    Handle<mirror::ClassLoader> class_loader,
+                                                    const DexFile::ClassDef* class_def,
+                                                    const DexFile::CodeItem* code_item,
+                                                    ArtMethod* method,
+                                                    uint32_t method_access_flags) {
   MethodVerifier* verifier = new MethodVerifier(self, dex_file, dex_cache, class_loader,
                                                 class_def, code_item, dex_method_idx, method,
                                                 method_access_flags, true, true, true, true);
   verifier->Verify();
-  verifier->DumpFailures(os);
-  os << verifier->info_messages_.str();
+  verifier->DumpFailures(vios->Stream());
+  vios->Stream() << verifier->info_messages_.str();
   // Only dump and return if no hard failures. Otherwise the verifier may be not fully initialized
   // and querying any info is dangerous/can abort.
   if (verifier->have_pending_hard_failure_) {
     delete verifier;
     return nullptr;
   } else {
-    verifier->Dump(os);
+    verifier->Dump(vios);
     return verifier;
   }
 }
@@ -1280,32 +1282,36 @@
 }
 
 void MethodVerifier::Dump(std::ostream& os) {
+  VariableIndentationOutputStream vios(&os);
+  Dump(&vios);
+}
+
+void MethodVerifier::Dump(VariableIndentationOutputStream* vios) {
   if (code_item_ == nullptr) {
-    os << "Native method\n";
+    vios->Stream() << "Native method\n";
     return;
   }
   {
-    os << "Register Types:\n";
-    Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-    std::ostream indent_os(&indent_filter);
-    reg_types_.Dump(indent_os);
+    vios->Stream() << "Register Types:\n";
+    ScopedIndentation indent1(vios);
+    reg_types_.Dump(vios->Stream());
   }
-  os << "Dumping instructions and register lines:\n";
-  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
-  std::ostream indent_os(&indent_filter);
+  vios->Stream() << "Dumping instructions and register lines:\n";
+  ScopedIndentation indent1(vios);
   const Instruction* inst = Instruction::At(code_item_->insns_);
   for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
       dex_pc += inst->SizeInCodeUnits()) {
     RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
     if (reg_line != nullptr) {
-      indent_os << reg_line->Dump(this) << "\n";
+      vios->Stream() << reg_line->Dump(this) << "\n";
     }
-    indent_os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].ToString() << " ";
+    vios->Stream()
+        << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].ToString() << " ";
     const bool kDumpHexOfInstruction = false;
     if (kDumpHexOfInstruction) {
-      indent_os << inst->DumpHex(5) << " ";
+      vios->Stream() << inst->DumpHex(5) << " ";
     }
-    indent_os << inst->DumpString(dex_file_) << "\n";
+    vios->Stream() << inst->DumpString(dex_file_) << "\n";
     inst = inst->Next();
   }
 }
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 2550694..d933448 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -32,6 +32,7 @@
 class Instruction;
 struct ReferenceMap2Visitor;
 class Thread;
+class VariableIndentationOutputStream;
 
 namespace verifier {
 
@@ -157,7 +158,9 @@
                                  bool allow_soft_failures, std::string* error)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static MethodVerifier* VerifyMethodAndDump(Thread* self, std::ostream& os, uint32_t method_idx,
+  static MethodVerifier* VerifyMethodAndDump(Thread* self,
+                                             VariableIndentationOutputStream* vios,
+                                             uint32_t method_idx,
                                              const DexFile* dex_file,
                                              Handle<mirror::DexCache> dex_cache,
                                              Handle<mirror::ClassLoader> class_loader,
@@ -191,6 +194,7 @@
   // Dump the state of the verifier, namely each instruction, what flags are set on it, register
   // information
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void Dump(VariableIndentationOutputStream* vios) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
   // to the locks held at 'dex_pc' in method 'm'.