Stack scanning: Find RegisterMap and unit-test it. Add decoding of the map.

Tests disabled, because there is a bug in System's LoadLibrary.

Change-Id: Ied3f4a31ce454f37c4d0f9caacd4ba03c4adb493
diff --git a/build/Android.common.mk b/build/Android.common.mk
index ce04683..f0e80b1 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -166,7 +166,8 @@
 
 LIBARTTEST_COMMON_SRC_FILES := \
 	src/base64.cc \
-	src/jni_tests.cc
+	src/jni_tests.cc \
+	src/stack_walk.cc
 
 TEST_COMMON_SRC_FILES := \
 	src/class_linker_test.cc \
@@ -215,6 +216,8 @@
 	Nested \
 	ProtoCompare \
 	ProtoCompare2 \
+	StackWalk \
+	StackWalk2 \
 	StaticLeafMethods \
 	Statics \
 	SystemMethods \
diff --git a/build/Android.libarttest.mk b/build/Android.libarttest.mk
index 6e1619d..823f78e 100644
--- a/build/Android.libarttest.mk
+++ b/build/Android.libarttest.mk
@@ -24,13 +24,16 @@
   LOCAL_MODULE := libarttest
   LOCAL_MODULE_TAGS := tests
   LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
+  LOCAL_SHARED_LIBRARIES := libartd
   ifeq ($(1),target)
     LOCAL_CFLAGS := $(ART_TARGET_CFLAGS) $(ART_TARGET_DEBUG_CFLAGS)
-    LOCAL_SHARED_LIBRARIES := libdl libstlport
+    LOCAL_SHARED_LIBRARIES += libdl libstlport
+    LOCAL_STATIC_LIBRARIES := libgtest
   else
     LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
-    LOCAL_LDLIBS := -ldl -lrt
+    LOCAL_LDLIBS := -ldl -lrt -lpthread
   endif
+  LOCAL_C_INCLUDES += $(ART_C_INCLUDES)
   ifeq ($(1),target)
     include $(BUILD_SHARED_LIBRARY)
   else
diff --git a/build/Android.oattest.mk b/build/Android.oattest.mk
index 5942fcf..48371a8 100644
--- a/build/Android.oattest.mk
+++ b/build/Android.oattest.mk
@@ -77,5 +77,8 @@
 $(eval $(call declare-test-test-target,Invoke,))
 $(eval $(call declare-test-test-target,ExceptionTest,))
 $(eval $(call declare-test-test-target,SystemMethods,))
+# TODO: Re-enable the test when System.LoadLibrary is working.
+# $(eval $(call declare-test-test-target,StackWalk,))
+# $(eval $(call declare-test-test-target,StackWalk2,))
 
 ########################################################################
diff --git a/src/stack_walk.cc b/src/stack_walk.cc
new file mode 100644
index 0000000..bc7b3a3
--- /dev/null
+++ b/src/stack_walk.cc
@@ -0,0 +1,114 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include <stdio.h>
+
+#include "UniquePtr.h"
+#include "class_linker.h"
+#include "common_test.h"
+#include "dex_verifier.h"
+// #include "heap.h"
+#include "object.h"
+#include "jni.h"
+
+namespace art {
+
+#define REG(method, reg_vector, reg) \
+  ( ((reg) < (method)->NumRegisters()) &&                       \
+    (( *((reg_vector) + (reg)/8) >> ((reg) % 8) ) & 0x01) )
+
+#define EXPECT_REGS(...) do {          \
+    int t[] = {__VA_ARGS__};             \
+    int t_size = sizeof(t) / sizeof(*t);      \
+    for (int i = 0; i < t_size; ++i)          \
+      EXPECT_TRUE(REG(m, reg_vector, t[i]));  \
+  } while(false)
+
+static int gJava_StackWalk_refmap_calls = 0;
+
+struct ReferenceMapVisitor : public Thread::StackVisitor {
+  ReferenceMapVisitor() {
+  }
+
+  void VisitFrame(const Frame& frame, uintptr_t pc) {
+    Method* m = frame.GetMethod();
+    if (!m ||m->IsNative()) {
+      return;
+    }
+    LOG(INFO) << "At " << PrettyMethod(m, false);
+
+    art::DexVerifier::RegisterMap* map = new art::DexVerifier::RegisterMap(
+        m->GetRegisterMapHeader(),
+        m->GetRegisterMapData());
+
+    if (!pc) {
+      // pc == NULL: m is either a native method or a phony method
+      return;
+    }
+    if (m->IsPhony()) {
+      LOG(WARNING) << "no PC for " << PrettyMethod(m);
+      return;
+    }
+
+    const uint8_t* reg_vector = art::DexVerifier::RegisterMapGetLine(map, m->ToDexPC(pc));
+    std::string m_name = m->GetName()->ToModifiedUtf8();
+
+    // Given the method name and the number of times the method has been called,
+    // we know the Dex registers with live reference values. Assert that what we
+    // find is what is expected.
+    if (m_name.compare("f") == 0) {
+      if (gJava_StackWalk_refmap_calls == 1) {
+        EXPECT_EQ(0U, m->ToDexPC(pc));
+        EXPECT_REGS(1);
+      } else {
+        CHECK(gJava_StackWalk_refmap_calls == 2);
+        EXPECT_EQ(4U, m->ToDexPC(pc));
+        EXPECT_REGS(1);  // Note that v1 is not in the minimal root set
+      }
+    } else if (m_name.compare("g") == 0) {
+      if (gJava_StackWalk_refmap_calls == 1) {
+        EXPECT_EQ(0xaU, m->ToDexPC(pc));
+        EXPECT_REGS(1, 2, 3);
+      } else {
+        CHECK(gJava_StackWalk_refmap_calls == 2);
+        EXPECT_EQ(0xaU, m->ToDexPC(pc));
+          EXPECT_REGS(1, 2, 3);
+      }
+    } else if (m_name.compare("shlemiel") == 0) {
+      if (gJava_StackWalk_refmap_calls == 1) {
+        EXPECT_EQ(0x2d5U, m->ToDexPC(pc));
+        EXPECT_REGS(2, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 21, 25);
+      } else {
+        CHECK(gJava_StackWalk_refmap_calls == 2);
+        EXPECT_EQ(0x2d5U, m->ToDexPC(pc));
+        EXPECT_REGS(2, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 21, 25);
+      }
+    }
+
+    LOG(INFO) << reg_vector;
+  }
+};
+
+extern "C"
+JNIEXPORT jint JNICALL Java_StackWalk_refmap(JNIEnv* env, jobject thisObj, jint count) {
+  EXPECT_EQ(count, 0);
+  gJava_StackWalk_refmap_calls++;
+
+  // Visitor
+  ReferenceMapVisitor mapper;
+  Thread::Current()->WalkStack(&mapper);
+
+  return count + 1;
+}
+
+extern "C"
+JNIEXPORT jint JNICALL Java_StackWalk_refmap2(JNIEnv* env, jobject thisObj, jint count) {
+  gJava_StackWalk_refmap_calls++;
+
+  // Visitor
+  ReferenceMapVisitor mapper;
+  Thread::Current()->WalkStack(&mapper);
+
+  return count + 1;
+}
+
+}
diff --git a/src/thread.h b/src/thread.h
index fe7900d..a9cc78f 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -531,6 +531,8 @@
     return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
   }
 
+  void WalkStack(StackVisitor* visitor) const;
+
  private:
   Thread();
   ~Thread();
@@ -557,8 +559,6 @@
 
   static void ThreadExitCallback(void* arg);
 
-  void WalkStack(StackVisitor* visitor) const;
-
   void WalkStackUntilUpCall(StackVisitor* visitor, bool include_upcall) const;
 
   // Thin lock thread id. This is a small integer used by the thin lock implementation.
diff --git a/test/StackWalk/StackWalk.java b/test/StackWalk/StackWalk.java
new file mode 100644
index 0000000..f7c78ff
--- /dev/null
+++ b/test/StackWalk/StackWalk.java
@@ -0,0 +1,93 @@
+public class StackWalk {
+  public StackWalk() {
+  }
+
+  int f() {
+    g(1);
+    g(2);
+    return 0;
+  }
+
+  void g(int num_calls) {
+    if (num_calls == 1) {
+      System.out.println("1st call");
+    } else if (num_calls == 2) {
+      System.out.println("2nd call");
+    }
+    System.out.println(shlemiel());
+  }
+
+  String shlemiel() {
+    String s0 = new String("0");
+    String s1 = new String("1");
+    String s2 = new String("2");
+    String s3 = new String("3");
+    String s4 = new String("4");
+    String s5 = new String("5");
+    String s6 = new String("6");
+    String s7 = new String("7");
+    String s8 = new String("8");
+    String s9 = new String("9");
+    String s10 = new String("10");
+    String s11 = new String("11");
+    String s12 = new String("12");
+    String s13 = new String("13");
+    String s14 = new String("14");
+    String s15 = new String("15");
+    String s16 = new String("16");
+    String s17 = new String("17");
+    String s18 = new String("18");
+    String s19 = new String("19");
+    String s20 = new String("20");
+    String s = new String();
+    s += s0;
+    s += s1;
+    s += s2;
+    s += s3;
+    s += s4;
+    s += s5;
+    s += s6;
+    s += s7;
+    s += s8;
+    s += s9;
+    s += s10;
+    s += s11;
+    s += s12;
+    s += s13;
+    s += s14;
+    s += s15;
+    s += s16;
+    s += s17;
+    s += s18;
+    s += s19;
+    s += s20;
+
+    s += s6;
+    s += s5;
+    s += s2;
+    s += s3;
+
+    s10 = s + s10;
+    s10 += s20;
+
+    s20 += s10;
+    s = s17 + s20;
+
+    s4 = s18 = s19;
+    s += s4;
+    s += s18;
+    refmap(0);
+    return s;
+  }
+
+  native int refmap(int x);
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  public static void main(String[] args) {
+    StackWalk st = new StackWalk();
+    st.f();
+  }
+}
diff --git a/test/StackWalk2/StackWalk2.java b/test/StackWalk2/StackWalk2.java
new file mode 100644
index 0000000..7d8c177
--- /dev/null
+++ b/test/StackWalk2/StackWalk2.java
@@ -0,0 +1,49 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.example.StackWalk2;
+
+public class StackWalk2 {
+  // use v1 for this
+
+  String str = new String();  // use v0 for str in <init>
+
+  int f() {
+    g(1);  // use v0 for 1, v1 for this
+    g(2);  // use v0 for 2, v1 for this
+    strTest();  // use v1 for this
+    return 0;
+  }
+
+  void g(int num_calls) throws RuntimeException {
+    if (num_calls == 1) {  // use v0 for 1, v3 for num_calls
+      System.logI("1st call");  // use v0 for PrintStream, v1 for "1st call"
+      refmap2(24);  // use v0 for 24, v2 for this
+    } else if (num_calls == 2) {  // use v0 for 2, v3 for num_calls
+      System.logI("2nd call");  // use v0 for PrintStream, v1 for "2nd call"
+      refmap2(25);  // use v0 for 24, v2 for this
+    }
+    throw new RuntimeException();  // use v0 for new RuntimeException
+  }
+
+  void strTest() {
+    System.logI(str);  // use v1 for PrintStream, v2, v3 for str
+    str = null;  // use v1 for null, v3 for str
+    str = new String("ya");  // use v2 for "ya", v1 for new String
+    String s = str;  // use v0, v1, v3
+    System.logI(str);  // use v1 for PrintStream, v2, v3 for str
+    System.logI(s);  // use v1 for PrintStream, v0 for s
+    s = null;  // use v0
+    System.logI(s);  // use v1 for PrintStream, v0 for s
+  }
+
+  native int refmap2(int x);
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  public static void main(String[] args) {
+    StackWalk2 st = new StackWalk2();
+    st.f();
+  }
+}