Stack Trace Element with its unit test added too.
The unit test passes.
Change-Id: I6f6af771cae1387c60c3b511148a347fe3237345
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 3907d07..3c72dad 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -30,6 +30,7 @@
"Ljava/lang/ClassLoader;",
"Ldalvik/system/BaseDexClassLoader;",
"Ldalvik/system/PathClassLoader;",
+ "Ljava/lang/StackTraceElement;",
"Z",
"B",
"C",
@@ -47,6 +48,7 @@
"[I",
"[J",
"[S",
+ "[Ljava/lang/StackTraceElement;",
};
ClassLinker* ClassLinker::Create(const std::vector<const DexFile*>& boot_class_path, Space* space) {
@@ -237,12 +239,15 @@
SetClassRoot(kFloatArrayClass, FindSystemClass("[F"));
SetClassRoot(kLongArrayClass, FindSystemClass("[J"));
SetClassRoot(kShortArrayClass, FindSystemClass("[S"));
+ SetClassRoot(kJavaLangStackTraceElement, FindSystemClass("Ljava/lang/StackTraceElement;"));
+ SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass("[Ljava/lang/StackTraceElement;"));
BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
+ StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
FinishInit();
}
@@ -342,6 +347,7 @@
LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
PathClassLoader::SetClass(GetClassRoot(kDalvikSystemPathClassLoader));
+ StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
FinishInit();
}
@@ -415,6 +421,7 @@
LongArray::ResetArrayClass();
ShortArray::ResetArrayClass();
PathClassLoader::ResetClass();
+ StackTraceElement::ResetClass();
}
DexCache* ClassLinker::AllocDexCache(const DexFile& dex_file) {
@@ -446,6 +453,12 @@
return down_cast<Method*>(GetClassRoot(kJavaLangReflectMethod)->NewInstance());
}
+ObjectArray<StackTraceElement>* ClassLinker::AllocStackTraceElementArray(size_t length) {
+ return ObjectArray<StackTraceElement>::Alloc(
+ GetClassRoot(kJavaLangStackTraceElementArrayClass),
+ length);
+}
+
Class* ClassLinker::FindClass(const StringPiece& descriptor,
const ClassLoader* class_loader) {
// TODO: remove this contrived parent class loader check when we have a real ClassLoader.
diff --git a/src/class_linker.h b/src/class_linker.h
index c848825..88e1a67 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -104,6 +104,8 @@
const DexFile& FindDexFile(const DexCache* dex_cache) const;
DexCache* FindDexCache(const DexFile& dex_file) const;
+ ObjectArray<StackTraceElement>* AllocStackTraceElementArray(size_t length);
+
private:
ClassLinker();
@@ -239,6 +241,7 @@
kJavaLangClassLoader,
kDalvikSystemBaseDexClassLoader,
kDalvikSystemPathClassLoader,
+ kJavaLangStackTraceElement,
kPrimitiveBoolean,
kPrimitiveByte,
kPrimitiveChar,
@@ -256,6 +259,7 @@
kIntArrayClass,
kLongArrayClass,
kShortArrayClass,
+ kJavaLangStackTraceElementArrayClass,
kClassRootsMax,
};
ObjectArray<Class>* class_roots_;
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 3489086..01eb099 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -532,7 +532,10 @@
return static_cast<ValueType>(type);
}
-String* DexFile::dexArtStringById(uint32_t idx) const {
+String* DexFile::dexArtStringById(int32_t idx) const {
+ if (idx == -1) {
+ return NULL;
+ }
return String::AllocFromModifiedUtf8(dexStringById(idx));
}
diff --git a/src/dex_file.h b/src/dex_file.h
index 2dcb3b2..db7a54b 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -535,7 +535,7 @@
return dexStringById(idx, &unicode_length);
}
- String* dexArtStringById(uint32_t idx) const;
+ String* dexArtStringById(int32_t idx) const;
// Get the descriptor string associated with a given type index.
const char* dexStringByTypeIdx(uint32_t idx, int32_t* unicode_length) const {
@@ -548,7 +548,7 @@
return dexStringById(type_id.descriptor_idx_);
}
- String* dexArtStringByTypeIdx(uint32_t idx) const {
+ String* dexArtStringByTypeIdx(int32_t idx) const {
const TypeId& type_id = GetTypeId(idx);
return dexArtStringById(type_id.descriptor_idx_);
}
diff --git a/src/exception.h b/src/exception.h
deleted file mode 100644
index 5b31e86..0000000
--- a/src/exception.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-
-#ifndef ART_SRC_EXCEPTION_H_
-#define ART_SRC_EXCEPTION_H_
-
-#include "class_linker.h"
-#include "dex_file.h"
-#include "object.h"
-#include "thread.h"
-
-namespace art {
-
-struct InternalStackTrace {
- const Method* method;
- const uint16_t* pc;
-};
-
-extern InternalStackTrace *GetStackTrace(uint16_t stack_depth);
-
-}
-
-#endif // ART_SRC_EXCEPTION_H_
diff --git a/src/exception_test.cc b/src/exception_test.cc
index 99fd954..4337d7a 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -73,8 +73,12 @@
ASSERT_TRUE(my_klass_ != NULL);
method_f_ = my_klass_->FindVirtualMethod("f", "()I");
ASSERT_TRUE(method_f_ != NULL);
+ method_f_->SetFrameSizeInBytes(8);
+ method_f_->SetReturnPcOffsetInBytes(4);
method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
ASSERT_TRUE(method_g_ != NULL);
+ method_g_->SetFrameSizeInBytes(8);
+ method_g_->SetReturnPcOffsetInBytes(4);
}
DexFile::CatchHandlerItem FindCatchHandlerItem(Method* method,
@@ -132,4 +136,36 @@
ASSERT_EQ(true, iter.HasNext());
}
+TEST_F(ExceptionTest, StackTraceElement) {
+ enum {STACK_SIZE = 1000};
+ uint32_t top_of_stack = 0;
+ uintptr_t fake_stack[STACK_SIZE];
+ fake_stack[top_of_stack++] = reinterpret_cast<uintptr_t>(method_g_);
+ fake_stack[top_of_stack++] = 3;
+ fake_stack[top_of_stack++] = reinterpret_cast<uintptr_t>(method_f_);
+ fake_stack[top_of_stack++] = 3;
+
+ Thread* thread = Thread::Current();
+ thread->SetTopOfStack(fake_stack);
+
+ Thread::InternalStackTrace* traces = thread->GetStackTrace(2);
+ ObjectArray<StackTraceElement>* trace_array = thread->GetStackTraceElement(2, traces);
+ delete[] traces;
+
+ ASSERT_TRUE(trace_array->Get(0) != NULL);
+ EXPECT_STREQ("java.lang.MyClass", trace_array->Get(0)->GetDeclaringClass()->ToModifiedUtf8().c_str());
+ EXPECT_STREQ("MyClass.java", trace_array->Get(0)->GetFileName()->ToModifiedUtf8().c_str());
+ EXPECT_STREQ("g", trace_array->Get(0)->GetMethodName()->ToModifiedUtf8().c_str());
+ EXPECT_EQ(22u, trace_array->Get(0)->GetLineNumber());
+
+ ASSERT_TRUE(trace_array->Get(1) != NULL);
+ EXPECT_STREQ("java.lang.MyClass", trace_array->Get(1)->GetDeclaringClass()->ToModifiedUtf8().c_str());
+ EXPECT_STREQ("MyClass.java", trace_array->Get(1)->GetFileName()->ToModifiedUtf8().c_str());
+ EXPECT_STREQ("f", trace_array->Get(1)->GetMethodName()->ToModifiedUtf8().c_str());
+ EXPECT_EQ(7u, trace_array->Get(1)->GetLineNumber());
+}
+
+// TODO: Test with native frame: For native frame, lineno should be -2 to
+// indicate it is native. That is how libcore tells from the StackTraceElement.
+
} // namespace art
diff --git a/src/object.cc b/src/object.cc
index 5d64466..175e687 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -602,6 +602,19 @@
dalvik_system_PathClassLoader_ = NULL;
}
+Class* StackTraceElement::java_lang_StackTraceElement_ = NULL;
+
+void StackTraceElement::SetClass(Class* java_lang_StackTraceElement) {
+ CHECK(java_lang_StackTraceElement_ == NULL);
+ CHECK(java_lang_StackTraceElement != NULL);
+ java_lang_StackTraceElement_ = java_lang_StackTraceElement;
+}
+
+void StackTraceElement::ResetClass() {
+ CHECK(java_lang_StackTraceElement_ != NULL);
+ java_lang_StackTraceElement_ = NULL;
+}
+
static const char* kClassStatusNames[] = {
"Error",
"NotReady",
diff --git a/src/object.h b/src/object.h
index 6c8fd6b..e306d74 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1681,6 +1681,53 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(Throwable);
};
+class StackTraceElement : public Object {
+ public:
+ const String* GetDeclaringClass() const {
+ return declaring_class_;
+ }
+
+ const String* GetMethodName() const {
+ return method_name_;
+ }
+
+ const String* GetFileName() const {
+ return file_name_;
+ }
+
+ uint32_t GetLineNumber() const {
+ return line_number_;
+ }
+
+ static StackTraceElement* Alloc(const String* declaring_class, const String* method_name,
+ const String* file_name, uint32_t line_number) {
+ StackTraceElement* trace = down_cast<StackTraceElement*>(GetStackTraceElement()->NewInstance());
+ trace->declaring_class_ = declaring_class;
+ trace->method_name_ = method_name;
+ trace->file_name_ = file_name;
+ trace->line_number_ = line_number;
+ return trace;
+ }
+
+ static void SetClass(Class* java_lang_StackTraceElement);
+
+ static void ResetClass();
+
+ private:
+ const String* declaring_class_;
+ const String* method_name_;
+ const String* file_name_;
+ uint32_t line_number_;
+
+ static Class* GetStackTraceElement() {
+ DCHECK(java_lang_StackTraceElement_ != NULL);
+ return java_lang_StackTraceElement_;
+ }
+
+ static Class* java_lang_StackTraceElement_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StackTraceElement);
+};
+
inline bool Object::IsString() const {
// TODO use "klass_ == String::GetJavaLangString()" instead?
return klass_ == klass_->descriptor_->klass_;
diff --git a/src/thread.cc b/src/thread.cc
index 2f43e23..047c89d 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -119,10 +119,10 @@
sp_ = reinterpret_cast<const Method**>(next_sp);
}
-void* Frame::GetPC() const {
+uintptr_t Frame::GetPC() const {
byte* pc_addr = reinterpret_cast<byte*>(sp_) +
GetMethod()->GetReturnPcOffsetInBytes();
- return reinterpret_cast<void*>(pc_addr);
+ return *reinterpret_cast<uintptr_t*>(pc_addr);
}
const Method* Frame::NextMethod() const {
@@ -309,6 +309,38 @@
return result;
}
+// TODO: Replaces trace.method and trace.pc with IntArray nad
+// ObjectArray<Method>.
+Thread::InternalStackTrace* Thread::GetStackTrace(uint16_t length) {
+ Frame frame = Thread::Current()->GetTopOfStack();
+ InternalStackTrace *traces = new InternalStackTrace[length];
+ for (uint16_t i = 0; i < length && frame.HasNext(); ++i, frame.Next()) {
+ traces[i].method = frame.GetMethod();
+ traces[i].pc = frame.GetPC();
+ }
+ return traces;
+}
+
+ObjectArray<StackTraceElement>* Thread::GetStackTraceElement(uint16_t length, InternalStackTrace *raw_trace) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjectArray<StackTraceElement>* java_traces = class_linker->AllocStackTraceElementArray(length);
+
+ for (uint16_t i = 0; i < length; ++i) {
+ // Prepare parameter for StackTraceElement(String cls, String method, String file, int line)
+ const Method* method = raw_trace[i].method;
+ const Class* klass = method->GetDeclaringClass();
+ const DexFile& dex_file = class_linker->FindDexFile(klass->GetDexCache());
+ String* readable_descriptor = String::AllocFromModifiedUtf8(PrettyDescriptor(klass->GetDescriptor()).c_str());
+
+ StackTraceElement* obj =
+ StackTraceElement::Alloc(readable_descriptor,
+ method->GetName(), String::AllocFromModifiedUtf8(klass->source_file_),
+ dex_file.GetLineNumFromPC(method, method->ToDexPC(raw_trace[i].pc)));
+ java_traces->Set(i, obj);
+ }
+ return java_traces;
+}
+
void Thread::ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...) {
std::string msg;
va_list args;
diff --git a/src/thread.h b/src/thread.h
index e8ca156..8a3a49b 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -27,6 +27,8 @@
class Thread;
class ThreadList;
class Throwable;
+class StackTraceElement;
+template<class T> class ObjectArray;
class Mutex {
public:
@@ -131,7 +133,7 @@
void Next();
- void* GetPC() const;
+ uintptr_t GetPC() const;
const Method** GetSP() const {
return sp_;
@@ -388,6 +390,16 @@
class_loader_override_ = class_loader_override;
}
+ struct InternalStackTrace {
+ const Method* method;
+ uintptr_t pc;
+ };
+
+ // Get the top length frames information
+ InternalStackTrace* GetStackTrace(uint16_t length);
+
+ ObjectArray<StackTraceElement>* GetStackTraceElement(uint16_t length, InternalStackTrace *raw_trace);
+
private:
Thread()
: id_(1234),