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();
+ }
+}