Add StackVisitor, CountStackDepthVisitor, BuildStackTraceVisitor for EH.
In addition to EH, GC can use StackVisitor too.
Crawl stack in 2 passes, using CountStackDepthVisitor and
BuildStackTraceVisitor, respectively. Note that Pass 1 computes
the depth with a doubly-nested loop.
Change-Id: If1e3d3212037426b10ac5e6a01138acfab710e6b
diff --git a/src/exception_test.cc b/src/exception_test.cc
index f2701dd..259c0a4 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -144,11 +144,13 @@
fake_stack[top_of_stack++] = 3;
fake_stack[top_of_stack++] = reinterpret_cast<uintptr_t>(method_f_);
fake_stack[top_of_stack++] = 3;
+ fake_stack[top_of_stack++] = NULL;
+ fake_stack[top_of_stack++] = 0;
Thread* thread = Thread::Current();
thread->SetTopOfStack(fake_stack);
- ObjectArray<StackTraceElement>* trace_array = thread->AllocStackTrace(2);
+ ObjectArray<StackTraceElement>* trace_array = thread->AllocStackTrace();
ASSERT_TRUE(trace_array->Get(0) != NULL);
EXPECT_STREQ("java.lang.MyClass",
diff --git a/src/thread.cc b/src/thread.cc
index 2e7b6a9..525e2a1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -135,7 +135,7 @@
const Method* Frame::NextMethod() const {
byte* next_sp = reinterpret_cast<byte*>(sp_) +
GetMethod()->GetFrameSizeInBytes();
- return reinterpret_cast<const Method*>(next_sp);
+ return *reinterpret_cast<const Method**>(next_sp);
}
void* ThreadStart(void *arg) {
@@ -316,32 +316,87 @@
return result;
}
-// TODO: Compute length
-bool Thread::WalkStack(uint16_t length,
- ObjectArray<Method>* method_trace,
- IntArray* pc_trace) {
- Frame frame = Thread::Current()->GetTopOfStack();
-
- for (uint16_t i = 0; i < length && frame.HasNext(); ++i, frame.Next()) {
- method_trace->Set(i, (Method*) frame.GetMethod());
- pc_trace->Set(i, frame.GetPC());
+class CountStackDepthVisitor : public Thread::StackVisitor {
+ public:
+ CountStackDepthVisitor() : depth(0) {}
+ virtual bool VisitFrame(const Frame&) {
+ ++depth;
+ return true;
}
- return true;
+
+ int GetDepth() const {
+ return depth;
+ }
+
+ private:
+ uint32_t depth;
+};
+
+class BuildStackTraceVisitor : public Thread::StackVisitor {
+ public:
+ BuildStackTraceVisitor(int depth) : count(0) {
+ method_trace = Runtime::Current()->GetClassLinker()->AllocObjectArray<const Method>(depth);
+ pc_trace = IntArray::Alloc(depth);
+ }
+
+ virtual ~BuildStackTraceVisitor() {}
+
+ virtual bool VisitFrame(const Frame& frame) {
+ 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);
+ }
+
+ private:
+ uint32_t count;
+ ObjectArray<const Method>* method_trace;
+ IntArray* pc_trace;
+};
+
+void Thread::WalkStack(StackVisitor* visitor) {
+ Frame frame = Thread::Current()->GetTopOfStack();
+ // TODO: enable this CHECK after native_to_managed_record_ is initialized during startup.
+ // CHECK(native_to_managed_record_ != NULL);
+ NativeToManagedRecord* record = native_to_managed_record_;
+
+ while (frame.GetSP()) {
+ for ( ; frame.GetMethod() != 0; frame.Next()) {
+ visitor->VisitFrame(frame);
+ }
+ if (record == NULL) {
+ break;
+ }
+ frame.SetSP(reinterpret_cast<const art::Method**>(record->last_top_of_managed_stack)); // last_tos should return Frame instead of sp?
+ record = record->link;
+ }
}
-ObjectArray<StackTraceElement>* Thread::AllocStackTrace(uint16_t length) {
+ObjectArray<StackTraceElement>* Thread::AllocStackTrace() {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- ObjectArray<Method>* method_trace = class_linker->AllocObjectArray<Method>(length);
- IntArray* pc_trace = IntArray::Alloc(length);
+ CountStackDepthVisitor count_visitor;
+ WalkStack(&count_visitor);
+ int32_t depth = count_visitor.GetDepth();
- WalkStack(length, method_trace, pc_trace);
+ BuildStackTraceVisitor build_trace_visitor(depth);
+ WalkStack(&build_trace_visitor);
- ObjectArray<StackTraceElement>* java_traces = class_linker->AllocStackTraceElementArray(length);
+ ObjectArray<StackTraceElement>* java_traces = class_linker->AllocStackTraceElementArray(depth);
- for (uint16_t i = 0; i < length; ++i) {
+ for (int32_t i = 0; i < depth; ++i) {
// Prepare parameter for StackTraceElement(String cls, String method, String file, int line)
- const Method* method = method_trace->Get(i);
+ const Method* method = build_trace_visitor.GetMethod(i);
const Class* klass = method->GetDeclaringClass();
const DexFile& dex_file = class_linker->FindDexFile(klass->GetDexCache());
String* readable_descriptor = String::AllocFromModifiedUtf8(
@@ -353,7 +408,7 @@
method->GetName(),
String::AllocFromModifiedUtf8(klass->source_file_),
dex_file.GetLineNumFromPC(method,
- method->ToDexPC(pc_trace->Get(i))));
+ method->ToDexPC(build_trace_visitor.GetPC(i))));
java_traces->Set(i, obj);
}
return java_traces;
diff --git a/src/thread.h b/src/thread.h
index 85abf2f..82cca6d 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -123,6 +123,7 @@
// Iterator over managed frames up to the first native-to-managed transition
class Frame {
+ public:
Frame() : sp_(NULL) {}
const Method* GetMethod() const {
@@ -222,6 +223,12 @@
void (*pArtThrowException)(struct Thread*, struct Object*);
void (*pArtHandleFillArrayDataNoThrow)(Array*, const uint16_t*);
+ class StackVisitor {
+ public:
+ virtual ~StackVisitor() {};
+ virtual bool VisitFrame(const Frame& frame) = 0;
+ };
+
// Creates a new thread.
static Thread* Create(const Runtime* runtime);
@@ -401,7 +408,7 @@
}
// Allocate stack trace
- ObjectArray<StackTraceElement>* AllocStackTrace(uint16_t length);
+ ObjectArray<StackTraceElement>* AllocStackTrace();
private:
Thread()
@@ -422,7 +429,7 @@
void InitCpu();
void InitFunctionPointers();
- bool WalkStack(uint16_t length, ObjectArray<Method>* method_trace, IntArray* pc_trace);
+ void WalkStack(StackVisitor* visitor);
// Managed thread id.
uint32_t id_;