ART: Make graph visualizer thread-safe

Store visualizer to a temporary string stream during compilation.
Print to the final output on destruction, and do so under a lock.

Note: the order of methods in the output is only guaranteed to be
      deterministic with single-threaded compilation (-j1).

Test: m
Test: Compile multithread
Test: Check output file that begin_compilation and end_compilation
      are interleaved
Test: Check that names of compiled methods match

Change-Id: I7eb3be2f88333cf1f116fe71b77cffa1ac702208
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 03870ab..8c76927 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -18,6 +18,8 @@
 
 #include <fstream>
 #include <memory>
+#include <sstream>
+
 #include <stdint.h>
 
 #ifdef ART_ENABLE_CODEGEN_arm
@@ -46,6 +48,7 @@
 #include "base/arena_containers.h"
 #include "base/dumpable.h"
 #include "base/macros.h"
+#include "base/mutex.h"
 #include "base/timing_logger.h"
 #include "bounds_check_elimination.h"
 #include "builder.h"
@@ -135,14 +138,18 @@
   PassObserver(HGraph* graph,
                CodeGenerator* codegen,
                std::ostream* visualizer_output,
-               CompilerDriver* compiler_driver)
+               CompilerDriver* compiler_driver,
+               Mutex& dump_mutex)
       : graph_(graph),
         cached_method_name_(),
         timing_logger_enabled_(compiler_driver->GetDumpPasses()),
         timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true),
         disasm_info_(graph->GetArena()),
+        visualizer_oss_(),
+        visualizer_output_(visualizer_output),
         visualizer_enabled_(!compiler_driver->GetCompilerOptions().GetDumpCfgFileName().empty()),
-        visualizer_(visualizer_output, graph, *codegen),
+        visualizer_(&visualizer_oss_, graph, *codegen),
+        visualizer_dump_mutex_(dump_mutex),
         graph_in_bad_state_(false) {
     if (timing_logger_enabled_ || visualizer_enabled_) {
       if (!IsVerboseMethod(compiler_driver, GetMethodName())) {
@@ -160,6 +167,10 @@
       LOG(INFO) << "TIMINGS " << GetMethodName();
       LOG(INFO) << Dumpable<TimingLogger>(timing_logger_);
     }
+    if (visualizer_enabled_) {
+      MutexLock mu(Thread::Current(), visualizer_dump_mutex_);
+      *visualizer_output_ << visualizer_oss_.str();
+    }
   }
 
   void DumpDisassembly() const {
@@ -237,8 +248,11 @@
 
   DisassemblyInformation disasm_info_;
 
+  std::ostringstream visualizer_oss_;
+  std::ostream* visualizer_output_;
   bool visualizer_enabled_;
   HGraphVisualizer visualizer_;
+  Mutex& visualizer_dump_mutex_;
 
   // Flag to be set by the compiler if the pass failed and the graph is not
   // expected to validate.
@@ -369,13 +383,16 @@
 
   std::unique_ptr<std::ostream> visualizer_output_;
 
+  mutable Mutex dump_mutex_;  // To synchronize visualizer writing.
+
   DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
 };
 
 static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
 
 OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
-    : Compiler(driver, kMaximumCompilationTimeBeforeWarning) {}
+    : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
+      dump_mutex_("Visualizer dump lock") {}
 
 void OptimizingCompiler::Init() {
   // Enable C1visualizer output. Must be done in Init() because the compiler
@@ -383,9 +400,6 @@
   CompilerDriver* driver = GetCompilerDriver();
   const std::string cfg_file_name = driver->GetCompilerOptions().GetDumpCfgFileName();
   if (!cfg_file_name.empty()) {
-    CHECK_EQ(driver->GetThreadCount(), 1U)
-      << "Graph visualizer requires the compiler to run single-threaded. "
-      << "Invoke the compiler with '-j1'.";
     std::ios_base::openmode cfg_file_mode =
         driver->GetCompilerOptions().GetDumpCfgAppend() ? std::ofstream::app : std::ofstream::out;
     visualizer_output_.reset(new std::ofstream(cfg_file_name, cfg_file_mode));
@@ -951,7 +965,8 @@
   PassObserver pass_observer(graph,
                              codegen.get(),
                              visualizer_output_.get(),
-                             compiler_driver);
+                             compiler_driver,
+                             dump_mutex_);
 
   VLOG(compiler) << "Building " << pass_observer.GetMethodName();