Dump the graph before/after every pass

We will now dump a partial CFG when encountering an error while
compiling which is useful for debugging a crash.

Also, force the number of threads to 1 if the --dump-cfg option is
passed. This makes the visualizer_dump_mutex_ obsolete since there's no
possibility of a race.

Bug: 203755436
Test: ART tests, dumped cfg with an added CHECK(false) to verify the
partial .cfg

Change-Id: I55bc9ceafef9e9eb4a43e891fb88d10e619bf64c
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6d7a953..c4dd31d 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -103,8 +103,7 @@
   PassObserver(HGraph* graph,
                CodeGenerator* codegen,
                std::ostream* visualizer_output,
-               const CompilerOptions& compiler_options,
-               Mutex& dump_mutex)
+               const CompilerOptions& compiler_options)
       : graph_(graph),
         last_seen_graph_size_(0),
         cached_method_name_(),
@@ -116,7 +115,6 @@
         visualizer_enabled_(!compiler_options.GetDumpCfgFileName().empty()),
         visualizer_(&visualizer_oss_, graph, codegen),
         codegen_(codegen),
-        visualizer_dump_mutex_(dump_mutex),
         graph_in_bad_state_(false) {
     if (timing_logger_enabled_ || visualizer_enabled_) {
       if (!IsVerboseMethod(compiler_options, GetMethodName())) {
@@ -143,6 +141,7 @@
   void DumpDisassembly() {
     if (visualizer_enabled_) {
       visualizer_.DumpGraphWithDisassembly();
+      FlushVisualizer();
     }
   }
 
@@ -162,14 +161,14 @@
     // Dump graph first, then start timer.
     if (visualizer_enabled_) {
       visualizer_.DumpGraph(pass_name, /* is_after_pass= */ false, graph_in_bad_state_);
+      FlushVisualizer();
     }
     if (timing_logger_enabled_) {
       timing_logger_.StartTiming(pass_name);
     }
   }
 
-  void FlushVisualizer() REQUIRES(!visualizer_dump_mutex_) {
-    MutexLock mu(Thread::Current(), visualizer_dump_mutex_);
+  void FlushVisualizer() {
     *visualizer_output_ << visualizer_oss_.str();
     visualizer_output_->flush();
     visualizer_oss_.str("");
@@ -183,6 +182,7 @@
     }
     if (visualizer_enabled_) {
       visualizer_.DumpGraph(pass_name, /* is_after_pass= */ true, graph_in_bad_state_);
+      FlushVisualizer();
     }
 
     // Validate the HGraph if running in debug mode.
@@ -231,7 +231,6 @@
   bool visualizer_enabled_;
   HGraphVisualizer visualizer_;
   CodeGenerator* codegen_;
-  Mutex& visualizer_dump_mutex_;
 
   // Flag to be set by the compiler if the pass failed and the graph is not
   // expected to validate.
@@ -406,8 +405,6 @@
 
   std::unique_ptr<std::ostream> visualizer_output_;
 
-  mutable Mutex dump_mutex_;  // To synchronize visualizer writing.
-
   DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
 };
 
@@ -415,8 +412,7 @@
 
 OptimizingCompiler::OptimizingCompiler(const CompilerOptions& compiler_options,
                                        CompiledMethodStorage* storage)
-    : Compiler(compiler_options, storage, kMaximumCompilationTimeBeforeWarning),
-      dump_mutex_("Visualizer dump lock") {
+    : Compiler(compiler_options, storage, kMaximumCompilationTimeBeforeWarning) {
   // Enable C1visualizer output.
   const std::string& cfg_file_name = compiler_options.GetDumpCfgFileName();
   if (!cfg_file_name.empty()) {
@@ -829,8 +825,7 @@
   PassObserver pass_observer(graph,
                              codegen.get(),
                              visualizer_output_.get(),
-                             compiler_options,
-                             dump_mutex_);
+                             compiler_options);
 
   {
     VLOG(compiler) << "Building " << pass_observer.GetMethodName();
@@ -953,8 +948,7 @@
   PassObserver pass_observer(graph,
                              codegen.get(),
                              visualizer_output_.get(),
-                             compiler_options,
-                             dump_mutex_);
+                             compiler_options);
 
   {
     VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName();
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 217afa9..22ac036 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1189,6 +1189,13 @@
       Usage(error_msg.c_str());
     }
 
+    if (!compiler_options_->GetDumpCfgFileName().empty() && thread_count_ != 1) {
+      LOG(INFO) << "Since we are dumping the CFG to " << compiler_options_->GetDumpCfgFileName()
+                << ", we override thread number to 1 to have determinism. It was " << thread_count_
+                << ".";
+      thread_count_ = 1;
+    }
+
     ProcessOptions(parser_options.get());
   }