Create stack traces in unstarted runtimes.

Use to diagnose failed initialization in dex2oat of boot (-verbose:compiler).
Fix identity hashCode, ArtMethod.getMethodName, IntegralToString.convertInt and
use of Void when called from a unstarted runtime.

Change-Id: I2d536174b59e2e5f19519f93fc6b5916652fb6cd
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index d393a13..6874fe5 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -24,6 +24,7 @@
 #include "object_array.h"
 #include "object_array-inl.h"
 #include "object_utils.h"
+#include "stack_trace_element.h"
 #include "utils.h"
 #include "well_known_classes.h"
 
@@ -53,6 +54,15 @@
   }
 }
 
+void Throwable::SetStackState(Object* state) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  CHECK(state != nullptr);
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFieldObjectVolatile<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), state);
+  } else {
+    SetFieldObjectVolatile<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), state);
+  }
+}
+
 bool Throwable::IsCheckedException() {
   if (InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error))) {
     return false;
@@ -70,24 +80,49 @@
   result += "\n";
   Object* stack_state = GetStackState();
   // check stack state isn't missing or corrupt
-  if (stack_state != NULL && stack_state->IsObjectArray()) {
+  if (stack_state != nullptr && stack_state->IsObjectArray()) {
     // Decode the internal stack trace into the depth and method trace
     ObjectArray<Object>* method_trace = down_cast<ObjectArray<Object>*>(stack_state);
     int32_t depth = method_trace->GetLength() - 1;
     IntArray* pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
     MethodHelper mh;
-    for (int32_t i = 0; i < depth; ++i) {
-      ArtMethod* method = down_cast<ArtMethod*>(method_trace->Get(i));
-      mh.ChangeMethod(method);
-      uint32_t dex_pc = pc_trace->Get(i);
-      int32_t line_number = mh.GetLineNumFromDexPC(dex_pc);
-      const char* source_file = mh.GetDeclaringClassSourceFile();
-      result += StringPrintf("  at %s (%s:%d)\n", PrettyMethod(method, true).c_str(),
-                             source_file, line_number);
+    if (depth == 0) {
+      result += "(Throwable with empty stack trace)";
+    } else {
+      for (int32_t i = 0; i < depth; ++i) {
+        ArtMethod* method = down_cast<ArtMethod*>(method_trace->Get(i));
+        mh.ChangeMethod(method);
+        uint32_t dex_pc = pc_trace->Get(i);
+        int32_t line_number = mh.GetLineNumFromDexPC(dex_pc);
+        const char* source_file = mh.GetDeclaringClassSourceFile();
+        result += StringPrintf("  at %s (%s:%d)\n", PrettyMethod(method, true).c_str(),
+                               source_file, line_number);
+      }
+    }
+  } else {
+    Object* stack_trace = GetStackTrace();
+    if (stack_trace != nullptr && stack_trace->IsObjectArray()) {
+      CHECK_EQ(stack_trace->GetClass()->GetComponentType(),
+               StackTraceElement::GetStackTraceElement());
+      ObjectArray<StackTraceElement>* ste_array =
+          down_cast<ObjectArray<StackTraceElement>*>(stack_trace);
+      if (ste_array->GetLength() == 0) {
+        result += "(Throwable with empty stack trace)";
+      } else {
+        for (int32_t i = 0; i < ste_array->GetLength(); ++i) {
+          StackTraceElement* ste = ste_array->Get(i);
+          result += StringPrintf("  at %s (%s:%d)\n",
+                                 ste->GetMethodName()->ToModifiedUtf8().c_str(),
+                                 ste->GetFileName()->ToModifiedUtf8().c_str(),
+                                 ste->GetLineNumber());
+        }
+      }
+    } else {
+      result += "(Throwable with no stack trace)";
     }
   }
   Throwable* cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_));
-  if (cause != NULL && cause != this) {  // Constructor makes cause == this by default.
+  if (cause != nullptr && cause != this) {  // Constructor makes cause == this by default.
     result += "Caused by: ";
     result += cause->Dump();
   }