Merge "Move FindDexMethodIndexInOtherDexFile into ArtMethod."
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 1993c23..0d5753c 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -19,6 +19,7 @@
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 #include <utils/Trace.h>
 
+#include <unordered_set>
 #include <vector>
 #include <unistd.h>
 
@@ -53,6 +54,7 @@
 #include "ScopedLocalRef.h"
 #include "handle_scope-inl.h"
 #include "thread.h"
+#include "thread_list.h"
 #include "thread_pool.h"
 #include "trampolines/trampoline_compiler.h"
 #include "transaction.h"
@@ -782,23 +784,141 @@
   }
 }
 
-void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void* arg) {
-  DCHECK(object != nullptr);
-  DCHECK(arg != nullptr);
-  CompilerDriver* compiler_driver = reinterpret_cast<CompilerDriver*>(arg);
-  StackHandleScope<1> hs(Thread::Current());
-  MaybeAddToImageClasses(hs.NewHandle(object->GetClass()), compiler_driver->image_classes_.get());
-}
+// Keeps all the data for the update together. Also doubles as the reference visitor.
+// Note: we can use object pointers because we suspend all threads.
+class ClinitImageUpdate {
+ public:
+  static ClinitImageUpdate* Create(std::set<std::string>* image_class_descriptors, Thread* self,
+                                   ClassLinker* linker, std::string* error_msg) {
+    std::unique_ptr<ClinitImageUpdate> res(new ClinitImageUpdate(image_class_descriptors, self,
+                                                                 linker));
+    if (res->art_method_class_ == nullptr) {
+      *error_msg = "Could not find ArtMethod class.";
+      return nullptr;
+    } else if (res->dex_cache_class_ == nullptr) {
+      *error_msg = "Could not find DexCache class.";
+      return nullptr;
+    }
+
+    return res.release();
+  }
+
+  ~ClinitImageUpdate() {
+    // Allow others to suspend again.
+    self_->EndAssertNoThreadSuspension(old_cause_);
+  }
+
+  // Visitor for VisitReferences.
+  void operator()(mirror::Object* object, MemberOffset field_offset, bool /* is_static */) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::Object* ref = object->GetFieldObject<mirror::Object>(field_offset);
+    if (ref != nullptr) {
+      VisitClinitClassesObject(ref);
+    }
+  }
+
+  // java.lang.Reference visitor for VisitReferences.
+  void operator()(mirror::Class* /*klass*/, mirror::Reference* ref) const {
+  }
+
+  void Walk() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Use the initial classes as roots for a search.
+    for (mirror::Class* klass_root : image_classes_) {
+      VisitClinitClassesObject(klass_root);
+    }
+  }
+
+ private:
+  ClinitImageUpdate(std::set<std::string>* image_class_descriptors, Thread* self,
+                    ClassLinker* linker)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+      image_class_descriptors_(image_class_descriptors), self_(self) {
+    CHECK(linker != nullptr);
+    CHECK(image_class_descriptors != nullptr);
+
+    // Make sure nobody interferes with us.
+    old_cause_ = self->StartAssertNoThreadSuspension("Boot image closure");
+
+    // Find the interesting classes.
+    art_method_class_ = linker->LookupClass(self, "Ljava/lang/reflect/ArtMethod;", nullptr);
+    dex_cache_class_ = linker->LookupClass(self, "Ljava/lang/DexCache;", nullptr);
+
+    // Find all the already-marked classes.
+    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    linker->VisitClasses(FindImageClasses, this);
+  }
+
+  static bool FindImageClasses(mirror::Class* klass, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ClinitImageUpdate* data = reinterpret_cast<ClinitImageUpdate*>(arg);
+    std::string temp;
+    const char* name = klass->GetDescriptor(&temp);
+    if (data->image_class_descriptors_->find(name) != data->image_class_descriptors_->end()) {
+      data->image_classes_.push_back(klass);
+    }
+
+    return true;
+  }
+
+  void VisitClinitClassesObject(mirror::Object* object) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(object != nullptr);
+    if (marked_objects_.find(object) != marked_objects_.end()) {
+      // Already processed.
+      return;
+    }
+
+    // Mark it.
+    marked_objects_.insert(object);
+
+    if (object->IsClass()) {
+      // If it is a class, add it.
+      StackHandleScope<1> hs(self_);
+      MaybeAddToImageClasses(hs.NewHandle(object->AsClass()), image_class_descriptors_);
+    } else {
+      // Else visit the object's class.
+      VisitClinitClassesObject(object->GetClass());
+    }
+
+    // If it is not a dex cache or an ArtMethod, visit all references.
+    mirror::Class* klass = object->GetClass();
+    if (klass != art_method_class_ && klass != dex_cache_class_) {
+      object->VisitReferences<false /* visit class */>(*this, *this);
+    }
+  }
+
+  mutable std::unordered_set<mirror::Object*> marked_objects_;
+  std::set<std::string>* const image_class_descriptors_;
+  std::vector<mirror::Class*> image_classes_;
+  const mirror::Class* art_method_class_;
+  const mirror::Class* dex_cache_class_;
+  Thread* const self_;
+  const char* old_cause_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClinitImageUpdate);
+};
 
 void CompilerDriver::UpdateImageClasses(TimingLogger* timings) {
   if (IsImage()) {
     TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
-    // Update image_classes_ with classes for objects created by <clinit> methods.
-    gc::Heap* heap = Runtime::Current()->GetHeap();
-    // TODO: Image spaces only?
-    ScopedObjectAccess soa(Thread::Current());
-    WriterMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
-    heap->VisitObjects(FindClinitImageClassesCallback, this);
+
+    Runtime* current = Runtime::Current();
+
+    // Suspend all threads.
+    current->GetThreadList()->SuspendAll();
+
+    std::string error_msg;
+    std::unique_ptr<ClinitImageUpdate> update(ClinitImageUpdate::Create(image_classes_.get(),
+                                                                        Thread::Current(),
+                                                                        current->GetClassLinker(),
+                                                                        &error_msg));
+    CHECK(update.get() != nullptr) << error_msg;  // TODO: Soft failure?
+
+    // Do the marking.
+    update->Walk();
+
+    // Resume threads.
+    current->GetThreadList()->ResumeAll();
   }
 }
 
@@ -1819,6 +1939,12 @@
               mirror::Throwable* exception = soa.Self()->GetException(&throw_location);
               VLOG(compiler) << "Initialization of " << descriptor << " aborted because of "
                   << exception->Dump();
+              std::ostream* file_log = manager->GetCompiler()->
+                  GetCompilerOptions().GetInitFailureOutput();
+              if (file_log != nullptr) {
+                *file_log << descriptor << "\n";
+                *file_log << exception->Dump() << "\n";
+              }
               soa.Self()->ClearException();
               transaction.Abort();
               CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 0592f0c..aec7d24 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
 #define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
 
+#include <ostream>
 #include <string>
 #include <vector>
 
@@ -70,7 +71,8 @@
 #ifdef ART_SEA_IR_MODE
     sea_ir_mode_(false),
 #endif
-    verbose_methods_(nullptr) {
+    verbose_methods_(nullptr),
+    init_failure_output_(nullptr) {
   }
 
   CompilerOptions(CompilerFilter compiler_filter,
@@ -90,7 +92,8 @@
 #ifdef ART_SEA_IR_MODE
                   bool sea_ir_mode,
 #endif
-                  const std::vector<std::string>* verbose_methods
+                  const std::vector<std::string>* verbose_methods,
+                  std::ostream* init_failure_output
                   ) :  // NOLINT(whitespace/parens)
     compiler_filter_(compiler_filter),
     huge_method_threshold_(huge_method_threshold),
@@ -109,7 +112,8 @@
 #ifdef ART_SEA_IR_MODE
     sea_ir_mode_(sea_ir_mode),
 #endif
-    verbose_methods_(verbose_methods) {
+    verbose_methods_(verbose_methods),
+    init_failure_output_(init_failure_output) {
   }
 
   CompilerFilter GetCompilerFilter() const {
@@ -217,6 +221,10 @@
     return false;
   }
 
+  std::ostream* GetInitFailureOutput() const {
+    return init_failure_output_;
+  }
+
  private:
   CompilerFilter compiler_filter_;
   const size_t huge_method_threshold_;
@@ -241,6 +249,9 @@
   // Vector of methods to have verbose output enabled for.
   const std::vector<std::string>* const verbose_methods_;
 
+  // Log initialization of initialization failures to this stream if not null.
+  std::ostream* const init_failure_output_;
+
   DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
 };
 std::ostream& operator<<(std::ostream& os, const CompilerOptions::CompilerFilter& rhs);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 869c822..cd78df1 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -709,6 +709,16 @@
         //       on having verbost methods.
         gLogVerbosity.compiler = false;
         Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
+      } else if (option.starts_with("--dump-init-failures=")) {
+        std::string file_name = option.substr(strlen("--dump-init-failures=")).data();
+        init_failure_output_.reset(new std::ofstream(file_name));
+        if (init_failure_output_.get() == nullptr) {
+          LOG(ERROR) << "Failed to allocate ofstream";
+        } else if (init_failure_output_->fail()) {
+          LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization "
+                     << "failures.";
+          init_failure_output_.reset();
+        }
       } else {
         Usage("Unknown argument %s", option.data());
       }
@@ -918,7 +928,8 @@
   #endif
                                                 verbose_methods_.empty() ?
                                                     nullptr :
-                                                    &verbose_methods_));
+                                                    &verbose_methods_,
+                                                init_failure_output_.get()));
 
     // Done with usage checks, enable watchdog if requested
     if (watch_dog_enabled) {
@@ -1652,6 +1663,7 @@
   std::string profile_file_;  // Profile file to use
   TimingLogger* timings_;
   std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
+  std::unique_ptr<std::ostream> init_failure_output_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index e30e745..7aed8b0 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -69,7 +69,12 @@
   // Cannot use this code to change into Runnable as changing to Runnable should fail if
   // old_state_and_flags.suspend_request is true.
   DCHECK_NE(new_state, kRunnable);
-  DCHECK_EQ(this, Thread::Current());
+  if (kIsDebugBuild && this != Thread::Current()) {
+    std::string name;
+    GetThreadName(name);
+    LOG(FATAL) << "Thread \"" << name << "\"(" << this << " != Thread::Current()="
+               << Thread::Current() << ") changing state to " << new_state;
+  }
   union StateAndFlags old_state_and_flags;
   old_state_and_flags.as_int = tls32_.state_and_flags.as_int;
   tls32_.state_and_flags.as_struct.state = new_state;
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 5ff90d6..beafcda 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -168,7 +168,9 @@
     const uint32_t kWaitTimeoutMs = 10000;
     bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kWaitTimeoutMs);
     if (timed_out) {
-      LOG(kIsDebugBuild ? FATAL : ERROR) << "Unexpected time out during dump checkpoint.";
+      // Avoid a recursive abort.
+      LOG((kIsDebugBuild && (gAborting == 0)) ? FATAL : ERROR)
+          << "Unexpected time out during dump checkpoint.";
     }
   }
 
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index d2cd8ab..5c0f83f 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -108,6 +108,11 @@
         DEV_MODE="y"
         TIME_OUT="n"
         shift
+    elif [ "x$1" = "x--gdb-arg" ]; then
+        shift
+        gdb_arg="$1"
+        GDB_ARGS="${GDB_ARGS} $gdb_arg"
+        shift
     elif [ "x$1" = "x--zygote" ]; then
         ZYGOTE="-Xzygote"
         msg "Spawning from zygote"
@@ -229,11 +234,11 @@
   else
     if [ `uname` = "Darwin" ]; then
         GDB=lldb
-        GDB_ARGS="-- $DALVIKVM"
+        GDB_ARGS="$GDB_ARGS -- $DALVIKVM"
         DALVIKVM=
     else
         GDB=gdb
-        GDB_ARGS="--args $DALVIKVM"
+        GDB_ARGS="$GDB_ARGS --args $DALVIKVM"
         # Enable for Emacs "M-x gdb" support. TODO: allow extra gdb arguments on command line.
         # gdbargs="--annotate=3 $gdbargs"
     fi
diff --git a/test/run-test b/test/run-test
index e9dd86a..6b8f007 100755
--- a/test/run-test
+++ b/test/run-test
@@ -171,6 +171,11 @@
         option="$1"
         run_args="${run_args} --runtime-option $option"
         shift
+    elif [ "x$1" = "x--gdb-arg" ]; then
+        shift
+        gdb_arg="$1"
+        run_args="${run_args} --gdb-arg $gdb_arg"
+        shift
     elif [ "x$1" = "x--debug" ]; then
         run_args="${run_args} --debug"
         shift
diff --git a/tools/analyze-init-failures.py b/tools/analyze-init-failures.py
new file mode 100755
index 0000000..f803ea3
--- /dev/null
+++ b/tools/analyze-init-failures.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2014 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.
+
+"""Analyzes the dump of initialization failures and creates a Graphviz dot file
+   representing dependencies."""
+
+import codecs
+import os
+import re
+import string
+import sys
+
+
+_CLASS_RE = re.compile(r'^L(.*);$')
+_ERROR_LINE_RE = re.compile(r'^java.lang.InternalError: (.*)')
+_STACK_LINE_RE = re.compile(r'^\s*at\s[^\s]*\s([^\s]*)')
+
+def Confused(filename, line_number, line):
+  sys.stderr.write('%s:%d: confused by:\n%s\n' % (filename, line_number, line))
+  raise Exception("giving up!")
+  sys.exit(1)
+
+
+def ProcessFile(filename):
+  lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
+  it = iter(lines)
+
+  class_fail_class = {}
+  class_fail_method = {}
+  root_failures = set()
+  root_errors = {}
+
+  while True:
+    try:
+      # We start with a class descriptor.
+      raw_line = it.next()
+      m = _CLASS_RE.search(raw_line)
+      # print(raw_line)
+      if m is None:
+        continue
+      # Found a class.
+      failed_clazz = m.group(1).replace('/','.')
+      # print('Is a class %s' % failed_clazz)
+      # The error line should be next.
+      raw_line = it.next()
+      m = _ERROR_LINE_RE.search(raw_line)
+      # print(raw_line)
+      if m is None:
+        Confused(filename, -1, raw_line)
+        continue
+      # Found an error line.
+      error = m.group(1)
+      # print('Is an error %s' % error)
+      # Get the top of the stack
+      raw_line = it.next()
+      m = _STACK_LINE_RE.search(raw_line)
+      if m is None:
+        continue
+      # Found a stack line. Get the method.
+      method = m.group(1)
+      # print('Is a stack element %s' % method)
+      (left_of_paren,paren,right_of_paren) = method.partition('(')
+      (root_err_class,dot,root_method_name) = left_of_paren.rpartition('.')
+      # print('Error class %s' % err_class)
+      # print('Error method %s' % method_name)
+      # Record the root error.
+      root_failures.add(root_err_class)
+      # Parse all the trace elements to find the "immediate" cause.
+      immediate_class = root_err_class
+      immediate_method = root_method_name
+      root_errors[root_err_class] = error
+      # Now go "up" the stack.
+      while True:
+        raw_line = it.next()
+        m = _STACK_LINE_RE.search(raw_line)
+        if m is None:
+          break  # Nothing more to see here.
+        method = m.group(1)
+        (left_of_paren,paren,right_of_paren) = method.partition('(')
+        (err_class,dot,err_method_name) = left_of_paren.rpartition('.')
+        if err_method_name == "<clinit>":
+          # A class initializer is on the stack...
+          class_fail_class[err_class] = immediate_class
+          class_fail_method[err_class] = immediate_method
+          immediate_class = err_class
+          immediate_method = err_method_name
+    except StopIteration:
+      # print('Done')
+      break  # Done
+
+  # Assign IDs.
+  fail_sources = set(class_fail_class.values());
+  all_classes = fail_sources | set(class_fail_class.keys())
+  i = 0
+  class_index = {}
+  for clazz in all_classes:
+    class_index[clazz] = i
+    i = i + 1
+
+  # Now create the nodes.
+  for (r_class, r_id) in class_index.items():
+    error_string = ''
+    if r_class in root_failures:
+      error_string = ',color=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"'
+    print('  n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string))
+
+  # Some space.
+  print('')
+
+  # Connections.
+  for (failed_class,error_class) in class_fail_class.items():
+    print('  n%d -> n%d;' % (class_index[failed_class], class_index[error_class]))
+
+
+def main():
+  print('digraph {')
+  print('  overlap=false;')
+  print('  splines=true;')
+  ProcessFile(sys.argv[1])
+  print('}')
+  sys.exit(0)
+
+
+if __name__ == '__main__':
+  main()