Implement Throwable::nativeFillInStackTrace and nativeGetStackTrace.

Refactor stack trace into compact internal form which is then used to
compute larger and more verbose StackTraceElement[] form. Fix some
potential problems with GC.

Change-Id: I4a4308c1ec5b82fd95b649d5ec0504b9607e688f
diff --git a/src/exception_test.cc b/src/exception_test.cc
index 5283697..d77f1c0 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -162,7 +162,13 @@
   Thread* thread = Thread::Current();
   thread->SetTopOfStack(fake_stack);
 
-  ObjectArray<StackTraceElement>* trace_array = thread->AllocStackTrace();
+  jobject internal = thread->CreateInternalStackTrace();
+  jobjectArray ste_array =
+      Thread::InternalStackTraceToStackTraceElementArray(internal,
+                                                         thread->GetJniEnv());
+  ObjectArray<StackTraceElement>* trace_array =
+      Decode<ObjectArray<StackTraceElement>*>(thread->GetJniEnv(), ste_array);
+
 
   ASSERT_TRUE(trace_array->Get(0) != NULL);
   EXPECT_STREQ("java.lang.MyClass",
diff --git a/src/java_lang_Throwable.cc b/src/java_lang_Throwable.cc
index 1d13907..4c3bfcf 100644
--- a/src/java_lang_Throwable.cc
+++ b/src/java_lang_Throwable.cc
@@ -15,7 +15,7 @@
  */
 
 #include "jni_internal.h"
-#include "object.h"
+#include "thread.h"
 
 #include "JniConstants.h" // Last to avoid problems with LOG redefinition.
 
@@ -24,22 +24,12 @@
 namespace {
 
 jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) {
-  UNIMPLEMENTED(WARNING);
-  //Object* stackState = dvmFillInStackTrace(dvmThreadSelf());
-  //return addLocalReference(env, stackState);
-  return NULL;
+  JNIEnvExt* env_ext = reinterpret_cast<JNIEnvExt*>(env);
+  return env_ext->self->CreateInternalStackTrace();
 }
 
 jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject javaStackState) {
-  UNIMPLEMENTED(WARNING);
-  //Object* stackState = dvmDecodeIndirectRef(env, javaStackState);
-  //if (stackState == NULL) {
-    //LOGW("getStackTrace() called but no trace available");
-    //return NULL;   /* could throw NPE; currently caller will do so */
-  //}
-  //ArrayObject* elements = dvmGetStackTrace(stackState);
-  //return reinterpret_cast<jobjectArray>(addLocalReference(env, elements));
-  return NULL;
+  return Thread::InternalStackTraceToStackTraceElementArray(javaStackState, env);
 }
 
 JNINativeMethod gMethods[] = {
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 216350b..880503f 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -14,6 +14,7 @@
 #include "jni_internal.h"
 #include "mem_map.h"
 #include "runtime.h"
+#include "scoped_jni_thread_state.h"
 #include "thread.h"
 
 namespace art {
@@ -413,12 +414,14 @@
 
 int gSuspendCounterHandler_calls;
 void SuspendCountHandler(Method** frame) {
+  // Check we came here in the native state then transition to runnable to work
+  // on the Object*
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
-  Thread::Current()->SetState(Thread::kRunnable);
+  ScopedJniThreadState ts(Thread::Current()->GetJniEnv());
+
   EXPECT_TRUE((*frame)->GetName()->Equals("fooI"));
   gSuspendCounterHandler_calls++;
   Thread::Current()->DecrementSuspendCount();
-  Thread::Current()->SetState(Thread::kNative);
 }
 
 TEST_F(JniCompilerTest, SuspendCountAcknowledgement) {
@@ -444,12 +447,14 @@
 
 int gExceptionHandler_calls;
 void ExceptionHandler(Method** frame) {
+  // Check we came here in the native state then transition to runnable to work
+  // on the Object*
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
-  Thread::Current()->SetState(Thread::kRunnable);
+  ScopedJniThreadState ts(Thread::Current()->GetJniEnv());
+
   EXPECT_TRUE((*frame)->GetName()->Equals("throwException"));
   gExceptionHandler_calls++;
   Thread::Current()->ClearException();
-  Thread::Current()->SetState(Thread::kNative);
 }
 
 void Java_MyClass_throwException(JNIEnv* env, jobject) {
@@ -480,19 +485,28 @@
 
 jint Java_MyClass_nativeUpCall(JNIEnv* env, jobject thisObj, jint i) {
   if (i <= 0) {
-    EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
-    Thread::Current()->SetState(Thread::kRunnable);
-    ObjectArray<StackTraceElement>* trace_array = Thread::Current()->AllocStackTrace();
+    // We want to check raw Object*/Array* below
+    ScopedJniThreadState ts(env);
+
+    // Build stack trace
+    jobject internal = Thread::Current()->CreateInternalStackTrace();
+    jobjectArray ste_array =
+        Thread::InternalStackTraceToStackTraceElementArray(internal, env);
+    ObjectArray<StackTraceElement>* trace_array =
+        Decode<ObjectArray<StackTraceElement>*>(env, ste_array);
     EXPECT_TRUE(trace_array != NULL);
     EXPECT_EQ(11, trace_array->GetLength());
 
+    // Check stack trace entries have expected values
     for (int32_t i = 0; i < trace_array->GetLength(); ++i) {
       EXPECT_EQ(-2, trace_array->Get(i)->GetLineNumber());
-      EXPECT_STREQ("MyClassNatives.java", trace_array->Get(i)->GetFileName()->ToModifiedUtf8().c_str());
-      EXPECT_STREQ("MyClass", trace_array->Get(i)->GetDeclaringClass()->ToModifiedUtf8().c_str());
-      EXPECT_STREQ("fooI", trace_array->Get(i)->GetMethodName()->ToModifiedUtf8().c_str());
+      StackTraceElement* ste = trace_array->Get(i);
+      EXPECT_STREQ("MyClassNatives.java", ste->GetFileName()->ToModifiedUtf8().c_str());
+      EXPECT_STREQ("MyClass", ste->GetDeclaringClass()->ToModifiedUtf8().c_str());
+      EXPECT_STREQ("fooI", ste->GetMethodName()->ToModifiedUtf8().c_str());
     }
-    Thread::Current()->SetState(Thread::kNative);
+
+    // end recursion
     return 0;
   } else {
     jclass jklass = env->FindClass("MyClass");
@@ -500,7 +514,10 @@
     jmethodID jmethod = env->GetMethodID(jklass, "fooI", "(I)I");
     EXPECT_TRUE(jmethod != NULL);
 
+    // Recurse with i - 1
     jint result = env->CallNonvirtualIntMethod(thisObj, jklass, jmethod, i - 1);
+
+    // Return sum of all depths
     return i + result;
   }
 }
@@ -508,7 +525,7 @@
 TEST_F(JniCompilerTest, NativeStackTraceElement) {
   SetupForTest(false, "fooI", "(I)I", reinterpret_cast<void*>(&Java_MyClass_nativeUpCall));
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 10);
-  EXPECT_EQ(55, result);
+  EXPECT_EQ(10+9+8+7+6+5+4+3+2+1, result);
 }
 
 jobject Java_MyClass_fooO(JNIEnv* env, jobject thisObj, jobject x) {
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 954fafd..0f464d2 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -111,6 +111,8 @@
 template ClassLoader* Decode<ClassLoader*>(JNIEnv*, jobject);
 template Object* Decode<Object*>(JNIEnv*, jobject);
 template String* Decode<String*>(JNIEnv*, jobject);
+template ObjectArray<StackTraceElement>*
+    Decode<ObjectArray<StackTraceElement>*>(JNIEnv*, jobject);
 
 namespace {
 
diff --git a/src/thread.cc b/src/thread.cc
index 4b477b8..312bb30 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -17,6 +17,7 @@
 #include "object.h"
 #include "runtime.h"
 #include "runtime_support.h"
+#include "scoped_jni_thread_state.h"
 #include "thread_list.h"
 #include "utils.h"
 
@@ -781,53 +782,65 @@
 
 class CountStackDepthVisitor : public Thread::StackVisitor {
  public:
-  CountStackDepthVisitor() : depth(0) {}
+  CountStackDepthVisitor() : depth_(0) {}
   virtual bool VisitFrame(const Frame&) {
-    ++depth;
+    ++depth_;
     return true;
   }
 
   int GetDepth() const {
-    return depth;
+    return depth_;
   }
 
  private:
-  uint32_t depth;
+  uint32_t depth_;
 };
 
-class BuildStackTraceVisitor : public Thread::StackVisitor {
+//
+class BuildInternalStackTraceVisitor : public Thread::StackVisitor {
  public:
-  explicit BuildStackTraceVisitor(int depth) : count(0) {
-    method_trace = Runtime::Current()->GetClassLinker()->AllocObjectArray<Method>(depth);
-    pc_trace = IntArray::Alloc(depth);
+  explicit BuildInternalStackTraceVisitor(int depth, ScopedJniThreadState& ts) : count_(0) {
+    // Allocate method trace with an extra slot that will hold the PC trace
+    method_trace_ = Runtime::Current()->GetClassLinker()->
+        AllocObjectArray<Object>(depth + 1);
+    // Register a local reference as IntArray::Alloc may trigger GC
+    local_ref_ = AddLocalReference<jobject>(ts.Env(), method_trace_);
+    pc_trace_ = IntArray::Alloc(depth);
+#ifdef MOVING_GARBAGE_COLLECTOR
+    // Re-read after potential GC
+    method_trace = Decode<ObjectArray<Object>*>(ts.Env(), local_ref_);
+#endif
+    // Save PC trace in last element of method trace, also places it into the
+    // object graph.
+    method_trace_->Set(depth, pc_trace_);
   }
 
-  virtual ~BuildStackTraceVisitor() {}
+  virtual ~BuildInternalStackTraceVisitor() {}
 
   virtual bool VisitFrame(const Frame& frame) {
-    method_trace->Set(count, frame.GetMethod());
-    pc_trace->Set(count, frame.GetPC());
-    ++count;
+    method_trace_->Set(count_, frame.GetMethod());
+    pc_trace_->Set(count_, frame.GetPC());
+    ++count_;
     return true;
   }
 
-  const Method* GetMethod(uint32_t i) {
-    DCHECK(i < count);
-    return method_trace->Get(i);
-  }
-
-  uintptr_t GetPC(uint32_t i) {
-    DCHECK(i < count);
-    return pc_trace->Get(i);
+  jobject GetInternalStackTrace() const {
+    return local_ref_;
   }
 
  private:
-  uint32_t count;
-  ObjectArray<Method>* method_trace;
-  IntArray* pc_trace;
+  // Current position down stack trace
+  uint32_t count_;
+  // Array of return PC values
+  IntArray* pc_trace_;
+  // An array of the methods on the stack, the last entry is a reference to the
+  // PC trace
+  ObjectArray<Object>* method_trace_;
+  // Local indirect reference table entry for method trace
+  jobject local_ref_;
 };
 
-void Thread::WalkStack(StackVisitor* visitor) {
+void Thread::WalkStack(StackVisitor* visitor) const {
   Frame frame = Thread::Current()->GetTopOfStack();
   // TODO: enable this CHECK after native_to_managed_record_ is initialized during startup.
   // CHECK(native_to_managed_record_ != NULL);
@@ -845,35 +858,65 @@
   }
 }
 
-ObjectArray<StackTraceElement>* Thread::AllocStackTrace() {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-
+jobject Thread::CreateInternalStackTrace() const {
+  // Compute depth of stack
   CountStackDepthVisitor count_visitor;
   WalkStack(&count_visitor);
   int32_t depth = count_visitor.GetDepth();
 
-  BuildStackTraceVisitor build_trace_visitor(depth);
+  // Transition into runnable state to work on Object*/Array*
+  ScopedJniThreadState ts(jni_env_);
+
+  // Build internal stack trace
+  BuildInternalStackTraceVisitor build_trace_visitor(depth, ts);
   WalkStack(&build_trace_visitor);
 
-  ObjectArray<StackTraceElement>* java_traces = class_linker->AllocStackTraceElementArray(depth);
+  return build_trace_visitor.GetInternalStackTrace();
+}
+
+jobjectArray Thread::InternalStackTraceToStackTraceElementArray(jobject internal,
+                                                                JNIEnv* env) {
+  // Transition into runnable state to work on Object*/Array*
+  ScopedJniThreadState ts(env);
+
+  // Decode the internal stack trace into the depth, method trace and PC trace
+  ObjectArray<Object>* method_trace =
+      down_cast<ObjectArray<Object>*>(Decode<Object*>(ts.Env(), internal));
+  int32_t depth = method_trace->GetLength()-1;
+  IntArray* pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+  // Create java_trace array and place in local reference table
+  ObjectArray<StackTraceElement>* java_traces =
+      class_linker->AllocStackTraceElementArray(depth);
+  jobjectArray result = AddLocalReference<jobjectArray>(ts.Env(), java_traces);
 
   for (int32_t i = 0; i < depth; ++i) {
-    // Prepare parameter for StackTraceElement(String cls, String method, String file, int line)
-    const Method* method = build_trace_visitor.GetMethod(i);
-    const Class* klass = method->GetDeclaringClass();
+    // Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
+    Method* method = down_cast<Method*>(method_trace->Get(i));
+    uint32_t native_pc = pc_trace->Get(i);
+    Class* klass = method->GetDeclaringClass();
     const DexFile& dex_file = class_linker->FindDexFile(klass->GetDexCache());
     String* readable_descriptor = String::AllocFromModifiedUtf8(
         PrettyDescriptor(klass->GetDescriptor()).c_str());
 
+    // Allocate element, potentially triggering GC
     StackTraceElement* obj =
         StackTraceElement::Alloc(readable_descriptor,
                                  method->GetName(),
                                  klass->GetSourceFile(),
                                  dex_file.GetLineNumFromPC(method,
-                                     method->ToDexPC(build_trace_visitor.GetPC(i))));
+                                     method->ToDexPC(native_pc)));
+#ifdef MOVING_GARBAGE_COLLECTOR
+    // Re-read after potential GC
+    java_traces = Decode<ObjectArray<Object>*>(ts.Env(), result);
+    method_trace = down_cast<ObjectArray<Object>*>(Decode<Object*>(ts.Env(), internal));
+    pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
+#endif
     java_traces->Set(i, obj);
   }
-  return java_traces;
+  return result;
 }
 
 void Thread::ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...) {
diff --git a/src/thread.h b/src/thread.h
index 3f6228a..ed4b1e4 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -400,8 +400,13 @@
     class_loader_override_ = class_loader_override;
   }
 
-  // Allocate stack trace
-  ObjectArray<StackTraceElement>* AllocStackTrace();
+  // Create the internal representation of a stack trace, that is more time
+  // and space efficient to compute than the StackTraceElement[]
+  jobject CreateInternalStackTrace() const;
+
+  // Convert an internal stack trace representation to a StackTraceElement[]
+  static jobjectArray
+      InternalStackTraceToStackTraceElementArray(jobject internal, JNIEnv* env);
 
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
@@ -475,7 +480,7 @@
 
   static void ThreadExitCallback(void* arg);
 
-  void WalkStack(StackVisitor* visitor);
+  void WalkStack(StackVisitor* visitor) const;
 
   // Thin lock thread id. This is a small integer used by the thin lock implementation.
   // This is not to be confused with the native thread's tid, nor is it the value returned