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/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index bde0fae..e5decc5 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1700,44 +1700,34 @@
               !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
           if (can_init_static_fields) {
             VLOG(compiler) << "Initializing: " << descriptor;
-            if (strcmp("Ljava/lang/Void;", descriptor) == 0) {
-              // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
-              ObjectLock<mirror::Class> lock(soa.Self(), &klass);
-              mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
-              CHECK_EQ(fields->GetLength(), 1);
-              fields->Get(0)->SetObj<false>(klass.get(),
-                                                     manager->GetClassLinker()->FindPrimitiveClass('V'));
-              klass->SetStatus(mirror::Class::kStatusInitialized, soa.Self());
-            } else {
-              // TODO multithreading support. We should ensure the current compilation thread has
-              // exclusive access to the runtime and the transaction. To achieve this, we could use
-              // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
-              // checks in Thread::AssertThreadSuspensionIsAllowable.
-              Runtime* const runtime = Runtime::Current();
-              Transaction transaction;
+            // TODO multithreading support. We should ensure the current compilation thread has
+            // exclusive access to the runtime and the transaction. To achieve this, we could use
+            // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
+            // checks in Thread::AssertThreadSuspensionIsAllowable.
+            Runtime* const runtime = Runtime::Current();
+            Transaction transaction;
 
-              // Run the class initializer in transaction mode.
-              runtime->EnterTransactionMode(&transaction);
-              const mirror::Class::Status old_status = klass->GetStatus();
-              bool success = manager->GetClassLinker()->EnsureInitialized(klass, true, true);
-              // TODO we detach transaction from runtime to indicate we quit the transactional
-              // mode which prevents the GC from visiting objects modified during the transaction.
-              // Ensure GC is not run so don't access freed objects when aborting transaction.
-              const char* old_casue = soa.Self()->StartAssertNoThreadSuspension("Transaction end");
-              runtime->ExitTransactionMode();
+            // Run the class initializer in transaction mode.
+            runtime->EnterTransactionMode(&transaction);
+            const mirror::Class::Status old_status = klass->GetStatus();
+            bool success = manager->GetClassLinker()->EnsureInitialized(klass, true, true);
+            // TODO we detach transaction from runtime to indicate we quit the transactional
+            // mode which prevents the GC from visiting objects modified during the transaction.
+            // Ensure GC is not run so don't access freed objects when aborting transaction.
+            const char* old_casue = soa.Self()->StartAssertNoThreadSuspension("Transaction end");
+            runtime->ExitTransactionMode();
 
-              if (!success) {
-                CHECK(soa.Self()->IsExceptionPending());
-                ThrowLocation throw_location;
-                mirror::Throwable* exception = soa.Self()->GetException(&throw_location);
-                VLOG(compiler) << "Initialization of " << descriptor << " aborted because of "
-                               << exception->Dump();
-                soa.Self()->ClearException();
-                transaction.Abort();
-                CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
-              }
-              soa.Self()->EndAssertNoThreadSuspension(old_casue);
+            if (!success) {
+              CHECK(soa.Self()->IsExceptionPending());
+              ThrowLocation throw_location;
+              mirror::Throwable* exception = soa.Self()->GetException(&throw_location);
+              VLOG(compiler) << "Initialization of " << descriptor << " aborted because of "
+                  << exception->Dump();
+              soa.Self()->ClearException();
+              transaction.Abort();
+              CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
             }
+            soa.Self()->EndAssertNoThreadSuspension(old_casue);
           }
         }
         soa.Self()->AssertNoPendingException();
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index da3e8ea..ee6a869 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -302,6 +302,8 @@
     CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
         << PrettyDescriptor(descriptor);
     result->SetL(found);
+  } else if (name == "java.lang.Class java.lang.Void.lookupType()") {
+    result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V'));
   } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") {
     SirtRef<ClassLoader> class_loader(self, down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset)));
     std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str()));
@@ -355,6 +357,12 @@
     args[0] = StackReference<mirror::Object>::FromMirrorPtr(found).AsVRegValue();
     EnterInterpreterFromInvoke(self, c, field.get(), args, NULL);
     result->SetL(field.get());
+  } else if (name == "int java.lang.Object.hashCode()") {
+    Object* obj = shadow_frame->GetVRegReference(arg_offset);
+    result->SetI(obj->IdentityHashCode());
+  } else if (name == "java.lang.String java.lang.reflect.ArtMethod.getMethodName(java.lang.reflect.ArtMethod)") {
+    ArtMethod* method = shadow_frame->GetVRegReference(arg_offset)->AsArtMethod();
+    result->SetL(MethodHelper(method).GetNameAsString());
   } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" ||
              name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") {
     // Special case array copying without initializing System.
@@ -381,7 +389,18 @@
         dst->Set(dstPos + i, src->Get(srcPos + i));
       }
     } else {
-      UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype);
+      self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
+                               "Unimplemented System.arraycopy for type '%s'",
+                               PrettyDescriptor(ctype).c_str());
+    }
+  } else  if (name == "java.lang.Object java.lang.ThreadLocal.get()") {
+    std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod()));
+    if (caller == "java.lang.String java.lang.IntegralToString.convertInt(java.lang.AbstractStringBuilder, int)") {
+      // Allocate non-threadlocal buffer.
+      result->SetL(mirror::CharArray::Alloc(self, 11));
+    } else {
+      self->ThrowNewException(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
+                              "Unimplemented ThreadLocal.get");
     }
   } else {
     // Not special, continue with regular interpreter execution.
diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h
index 1acbbb0..c324d96 100644
--- a/runtime/mirror/stack_trace_element.h
+++ b/runtime/mirror/stack_trace_element.h
@@ -57,6 +57,10 @@
   static void ResetClass();
   static void VisitRoots(RootCallback* callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static Class* GetStackTraceElement() {
+    DCHECK(java_lang_StackTraceElement_ != NULL);
+    return java_lang_StackTraceElement_;
+  }
 
  private:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
@@ -70,11 +74,6 @@
             SirtRef<String>& file_name, int32_t line_number)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static Class* GetStackTraceElement() {
-    DCHECK(java_lang_StackTraceElement_ != NULL);
-    return java_lang_StackTraceElement_;
-  }
-
   static Class* java_lang_StackTraceElement_;
 
   friend struct art::StackTraceElementOffsets;  // for verifying offset information
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();
   }
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index 950b5e7..c4127e0 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -42,6 +42,7 @@
   // overridden. Also it asserts rather than throwing exceptions. Currently this is only used
   // in cases like the verifier where the checks cannot fail and initCause isn't overridden.
   void SetCause(Throwable* cause) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetStackState(Object* state) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool IsCheckedException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static Class* GetJavaLangThrowable() {
@@ -58,6 +59,9 @@
   Object* GetStackState() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_));
   }
+  Object* GetStackTrace() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_trace_));
+  }
 
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   HeapReference<Throwable> cause_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index e67a64f..23a6779 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1639,6 +1639,13 @@
     if (cause.get() != nullptr) {
       exception->SetCause(down_cast<mirror::Throwable*>(DecodeJObject(cause.get())));
     }
+    ScopedLocalRef<jobject> trace(GetJniEnv(),
+                                  Runtime::Current()->IsActiveTransaction()
+                                      ? CreateInternalStackTrace<true>(soa)
+                                      : CreateInternalStackTrace<false>(soa));
+    if (trace.get() != nullptr) {
+      exception->SetStackState(down_cast<mirror::Throwable*>(DecodeJObject(trace.get())));
+    }
     ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(),
                                          throw_location.GetDexPc());
     SetException(gc_safe_throw_location, exception.get());