More SIGQUIT detail, earlier exit in AttachCurrentThread, handling recursive aborts, and reporting debug/optimized builds.

Change-Id: I932b49a8e92a91e340b4d98b8771dd4a41b61229
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9d8a1f1..a94607a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -2800,6 +2800,12 @@
   }
 }
 
+void ClassLinker::DumpForSigQuit(std::ostream& os) const {
+  MutexLock mu(classes_lock_);
+  os << "Loaded classes: " << image_classes_.size() << " image classes; "
+     << classes_.size() << " allocated classes\n";
+}
+
 size_t ClassLinker::NumLoadedClasses() const {
   MutexLock mu(classes_lock_);
   return classes_.size() + image_classes_.size();
diff --git a/src/class_linker.h b/src/class_linker.h
index a972b3b..03c8341 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -70,6 +70,8 @@
 
   void DumpAllClasses(int flags) const;
 
+  void DumpForSigQuit(std::ostream& os) const;
+
   size_t NumLoadedClasses() const;
 
   // Resolve a String with the given index from the DexFile, storing the
diff --git a/src/intern_table.cc b/src/intern_table.cc
index 5de70d8..a848761 100644
--- a/src/intern_table.cc
+++ b/src/intern_table.cc
@@ -15,6 +15,13 @@
   return strong_interns_.size() + weak_interns_.size();
 }
 
+void InternTable::DumpForSigQuit(std::ostream& os) const {
+  MutexLock mu(intern_table_lock_);
+  os << "Intern table: " << strong_interns_.size() << " strong; "
+     << weak_interns_.size() << " weak; "
+     << image_strong_interns_.size() << " image strong\n";
+}
+
 void InternTable::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
   MutexLock mu(intern_table_lock_);
   typedef Table::const_iterator It; // TODO: C++0x auto
diff --git a/src/intern_table.h b/src/intern_table.h
index 86054b9..80f21e2 100644
--- a/src/intern_table.h
+++ b/src/intern_table.h
@@ -3,11 +3,12 @@
 #ifndef ART_SRC_INTERN_TABLE_H_
 #define ART_SRC_INTERN_TABLE_H_
 
-#include "unordered_map.h"
+#include <iosfwd>
 
 #include "heap.h"
 #include "mutex.h"
 #include "object.h"
+#include "unordered_map.h"
 
 namespace art {
 
@@ -49,6 +50,8 @@
 
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
+  void DumpForSigQuit(std::ostream& os) const;
+
  private:
   typedef std::tr1::unordered_multimap<int32_t, String*> Table;
 
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 1f3124c..45eedb8 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -447,6 +447,21 @@
     return JNI_ERR;
   }
 
+  // Return immediately if we're already one with the VM.
+  Thread* self = Thread::Current();
+  if (self != NULL) {
+    *p_env = self->GetJniEnv();
+    return JNI_OK;
+  }
+
+  Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
+
+  // No threads allowed in zygote mode.
+  if (runtime->IsZygote()) {
+    LOG(ERROR) << "Attempt to attach a thread in the zygote";
+    return JNI_ERR;
+  }
+
   JavaVMAttachArgs* in_args = static_cast<JavaVMAttachArgs*>(thr_args);
   JavaVMAttachArgs args;
   if (thr_args == NULL) {
@@ -466,7 +481,6 @@
   }
   CHECK_GE(args.version, JNI_VERSION_1_2);
 
-  Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
   runtime->AttachCurrentThread(args.name, as_daemon);
   *p_env = Thread::Current()->GetJniEnv();
   return JNI_OK;
diff --git a/src/runtime.cc b/src/runtime.cc
index 54d7ec6..c1060f7 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -76,8 +76,15 @@
   instance_ = NULL;
 }
 
+static bool gAborting = false;
+
 struct AbortState {
   void Dump(std::ostream& os) {
+    if (gAborting) {
+      os << "Runtime aborting --- recursively, so no thread-specific detail!\n";
+      return;
+    }
+    gAborting = true;
     os << "Runtime aborting...\n";
     Thread* self = Thread::Current();
     if (self == NULL) {
@@ -372,7 +379,13 @@
     parsed->heap_growth_limit_ = parsed->heap_maximum_size_;
   }
 
-  LOG(INFO) << "CheckJNI is " << (parsed->check_jni_ ? "on" : "off");
+  LOG(INFO) << "Build type: "
+#ifndef NDEBUG
+            << "debug"
+#else
+            << "optimized"
+#endif
+            << "; CheckJNI: " << (parsed->check_jni_ ? "on" : "off");
 
   return parsed.release();
 }
@@ -612,14 +625,8 @@
 
 void Runtime::Dump(std::ostream& os) {
   // TODO: dump other runtime statistics?
-  os << "Loaded classes: " << class_linker_->NumLoadedClasses() << "\n";
-  os << "Intern table size: " << GetInternTable()->Size() << "\n";
-  // LOGV("VM stats: meth=%d ifld=%d sfld=%d linear=%d",
-  //    gDvm.numDeclaredMethods,
-  //    gDvm.numDeclaredInstFields,
-  //    gDvm.numDeclaredStaticFields,
-  //    gDvm.pBootLoaderAlloc->curOffset);
-  // LOGI("GC precise methods: %d", dvmPointerSetGetCount(gDvm.preciseMethods));
+  GetClassLinker()->DumpForSigQuit(os);
+  GetInternTable()->DumpForSigQuit(os);
   os << "\n";
 
   thread_list_->Dump(os);
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index 4e8628d..f25a9ba 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -105,7 +105,6 @@
 
   std::ostringstream os;
   os << "\n"
-     << "\n"
      << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
 
   std::string cmdline;
diff --git a/src/thread.cc b/src/thread.cc
index 538aaa8..e43aa14 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -246,6 +246,8 @@
 }
 
 void Thread::Attach(const Runtime* runtime) {
+  CHECK(Thread::Current() == NULL);
+
   InitCpu();
   InitFunctionPointers();
   InitCardTable();
@@ -263,21 +265,18 @@
 }
 
 Thread* Thread::Attach(const Runtime* runtime, const char* name, bool as_daemon) {
-  Thread* self = Thread::Current();
-  if (self == NULL) {
-    self = new Thread;
-    self->Attach(runtime);
+  Thread* self = new Thread;
+  self->Attach(runtime);
 
-    self->SetState(Thread::kNative);
+  self->SetState(Thread::kNative);
 
-    // If we're the main thread, ClassLinker won't be created until after we're attached,
-    // so that thread needs a two-stage attach. Regular threads don't need this hack.
-    if (self->thin_lock_id_ != ThreadList::kMainId) {
-      self->CreatePeer(name, as_daemon);
-    }
-
-    self->GetJniEnv()->locals.AssertEmpty();
+  // If we're the main thread, ClassLinker won't be created until after we're attached,
+  // so that thread needs a two-stage attach. Regular threads don't need this hack.
+  if (self->thin_lock_id_ != ThreadList::kMainId) {
+    self->CreatePeer(name, as_daemon);
   }
+
+  self->GetJniEnv()->locals.AssertEmpty();
   return self;
 }