Don't pre-allocate one OOME per thread.

There's no associated stack trace, so we can share one between all
threads, saving a little memory and thread start-up time. Speaking
of stack traces: dump the stack to the log at the point where we
realize we're not going to be able to throw an exception with a
stack, so the developer has _something_ to work with.

Change-Id: I2246d291855bd9b9ee13f2be5b1ce14f669e9410
diff --git a/src/runtime.cc b/src/runtime.cc
index 1a66951..dda8f62 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -63,6 +63,7 @@
       class_linker_(NULL),
       signal_catcher_(NULL),
       java_vm_(NULL),
+      pre_allocated_OutOfMemoryError_(NULL),
       jni_stub_array_(NULL),
       abstract_method_error_stub_array_(NULL),
       resolution_method_(NULL),
@@ -82,7 +83,7 @@
     resolution_stub_array_[i] = NULL;
   }
   for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
-    callee_save_method_[i] = NULL;
+    callee_save_methods_[i] = NULL;
   }
 }
 
@@ -549,11 +550,18 @@
 
   CHECK(host_prefix_.empty()) << host_prefix_;
 
-  // Relocate the OatFiles (ELF images)
+  // Relocate the OatFiles (ELF images).
   class_linker_->RelocateExecutable();
 
-  // Restore main thread state to kNative as expected by native code
-  Thread::Current()->SetState(kNative);
+  // Restore main thread state to kNative as expected by native code.
+  Thread* self = Thread::Current();
+  self->SetState(kNative);
+
+  // Pre-allocate an OutOfMemoryError for the double-OOME case.
+  self->ThrowNewException("Ljava/lang/OutOfMemoryError;",
+                          "OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack available");
+  pre_allocated_OutOfMemoryError_ = self->GetException();
+  self->ClearException();
 
   started_ = true;
 
@@ -571,7 +579,7 @@
 
   CreateSystemClassLoader();
 
-  Thread::Current()->GetJniEnv()->locals.AssertEmpty();
+  self->GetJniEnv()->locals.AssertEmpty();
 
   VLOG(startup) << "Runtime::Start exiting";
 
@@ -851,6 +859,9 @@
   intern_table_->VisitRoots(visitor, arg);
   java_vm_->VisitRoots(visitor, arg);
   thread_list_->VisitRoots(visitor, arg);
+  if (pre_allocated_OutOfMemoryError_ != NULL) {
+    visitor(pre_allocated_OutOfMemoryError_, arg);
+  }
   visitor(jni_stub_array_, arg);
   visitor(abstract_method_error_stub_array_, arg);
   for (int i = 0; i < Runtime::kLastTrampolineMethodType; i++) {
@@ -858,7 +869,7 @@
   }
   visitor(resolution_method_, arg);
   for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
-    visitor(callee_save_method_[i], arg);
+    visitor(callee_save_methods_[i], arg);
   }
 }
 
@@ -943,7 +954,7 @@
 
 void Runtime::SetCalleeSaveMethod(Method* method, CalleeSaveType type) {
   DCHECK_LT(static_cast<int>(type), static_cast<int>(kLastCalleeSaveType));
-  callee_save_method_[type] = method;
+  callee_save_methods_[type] = method;
 }
 
 void Runtime::EnableMethodTracing(Trace* tracer) {
diff --git a/src/runtime.h b/src/runtime.h
index d80f1c9..9181f1f 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -48,6 +48,7 @@
 class SignalCatcher;
 class String;
 class ThreadList;
+class Throwable;
 class Trace;
 
 class Runtime {
@@ -172,14 +173,18 @@
     return java_vm_;
   }
 
-  const std::vector<std::string>& GetProperties() const {
-    return properties_;
-  }
-
   MonitorList* GetMonitorList() const {
     return monitor_list_;
   }
 
+  Throwable* GetPreAllocatedOutOfMemoryError() {
+    return pre_allocated_OutOfMemoryError_;
+  }
+
+  const std::vector<std::string>& GetProperties() const {
+    return properties_;
+  }
+
   ThreadList* GetThreadList() const {
     return thread_list_;
   }
@@ -255,12 +260,12 @@
   };
 
   bool HasCalleeSaveMethod(CalleeSaveType type) const {
-    return callee_save_method_[type] != NULL;
+    return callee_save_methods_[type] != NULL;
   }
 
   Method* GetCalleeSaveMethod(CalleeSaveType type) const {
     CHECK(HasCalleeSaveMethod(type));
-    return callee_save_method_[type];
+    return callee_save_methods_[type];
   }
 
   void SetCalleeSaveMethod(Method* method, CalleeSaveType type);
@@ -359,13 +364,15 @@
 
   JavaVMExt* java_vm_;
 
+  Throwable* pre_allocated_OutOfMemoryError_;
+
   ByteArray* jni_stub_array_;
 
   ByteArray* abstract_method_error_stub_array_;
 
   ByteArray* resolution_stub_array_[kLastTrampolineMethodType];
 
-  Method* callee_save_method_[kLastCalleeSaveType];
+  Method* callee_save_methods_[kLastCalleeSaveType];
 
   Method* resolution_method_;
 
diff --git a/src/thread.cc b/src/thread.cc
index f6b9a44..b263039 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -339,17 +339,10 @@
     DecodeField(WellKnownClasses::java_lang_Thread_priority)->SetInt(peer_, thread_priority);
     peer_thread_name.reset(GetThreadName());
   }
-  // thread_name may have been null, so don't trust this to be non-null
+  // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null.
   if (peer_thread_name.get() != NULL) {
     SetThreadName(peer_thread_name->ToModifiedUtf8().c_str());
   }
-
-  // Pre-allocate an OutOfMemoryError for the double-OOME case.
-  ThrowNewException("Ljava/lang/OutOfMemoryError;",
-      "OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack available");
-  ScopedLocalRef<jthrowable> exception(env, env->ExceptionOccurred());
-  env->ExceptionClear();
-  pre_allocated_OutOfMemoryError_ = Decode<Throwable*>(env, exception.get());
 }
 
 void Thread::SetThreadName(const char* name) {
@@ -842,7 +835,6 @@
       class_loader_override_(NULL),
       long_jump_context_(NULL),
       throwing_OutOfMemoryError_(false),
-      pre_allocated_OutOfMemoryError_(NULL),
       debug_invoke_req_(new DebugInvokeReq),
       trace_stack_(new std::vector<TraceStackFrame>),
       name_(new std::string(kThreadNameDuringStartup)) {
@@ -1487,12 +1479,12 @@
     throwing_OutOfMemoryError_ = true;
     ThrowNewException("Ljava/lang/OutOfMemoryError;", NULL);
   } else {
-    SetException(pre_allocated_OutOfMemoryError_);
+    Dump(LOG(ERROR)); // The pre-allocated OOME has no stack, so help out and log one.
+    SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
   }
   throwing_OutOfMemoryError_ = false;
 }
 
-
 Thread* Thread::CurrentFromGdb() {
   return Thread::Current();
 }
@@ -1887,9 +1879,6 @@
   if (peer_ != NULL) {
     visitor(peer_, arg);
   }
-  if (pre_allocated_OutOfMemoryError_ != NULL) {
-    visitor(pre_allocated_OutOfMemoryError_, arg);
-  }
   if (class_loader_override_ != NULL) {
     visitor(class_loader_override_, arg);
   }
diff --git a/src/thread.h b/src/thread.h
index 6a2b27e..ef539c0 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -628,8 +628,6 @@
   // A boolean telling us whether we're recursively throwing OOME.
   uint32_t throwing_OutOfMemoryError_;
 
-  Throwable* pre_allocated_OutOfMemoryError_;
-
   // JDWP invoke-during-breakpoint support.
   DebugInvokeReq* debug_invoke_req_;
 
diff --git a/src/utils.cc b/src/utils.cc
index 87c8704..084456d 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -958,7 +958,7 @@
     os << prefix << "(unwind_backtrace_thread failed for thread " << tid << ")\n";
     return;
   } else if (frame_count == 0) {
-    os << prefix << "(no native stack frames)\n";
+    os << prefix << "(no native stack frames for thread " << tid << ")\n";
     return;
   }