diff options
| author | 2011-08-14 17:47:36 -0700 | |
|---|---|---|
| committer | 2011-08-23 10:17:07 -0700 | |
| commit | 1a18c8c1c0e4ea1ff06177e93c7ff703376dcee2 (patch) | |
| tree | 1a37b77910cbb6c628113b61175d7011b7e6a6cd /src | |
| parent | 37f7a40f6789bb287f287a9af00777af9d6428ee (diff) | |
Initial implementation of throw+unwind+Frame.
Unit tests on Exception Handling pass.
Change-Id: I33505dacc6648e72e2ed80c2cca83643cfaef86b
Diffstat (limited to 'src')
| -rw-r--r-- | src/class_linker.h | 2 | ||||
| -rw-r--r-- | src/dex_file.h | 2 | ||||
| -rw-r--r-- | src/exception.h | 22 | ||||
| -rw-r--r-- | src/exception_test.cc | 74 | ||||
| -rw-r--r-- | src/object.h | 31 | ||||
| -rw-r--r-- | src/thread.cc | 79 | ||||
| -rw-r--r-- | src/thread.h | 68 |
7 files changed, 243 insertions, 35 deletions
diff --git a/src/class_linker.h b/src/class_linker.h index 5b629d5e28..28dfce7439 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -271,7 +271,7 @@ class ClassLinker { FRIEND_TEST(DexCacheTest, Open); friend class ObjectTest; FRIEND_TEST(ObjectTest, AllocObjectArray); - FRIEND_TEST(ExceptionTest, MyClass_F_G); + FRIEND_TEST(ExceptionTest, FindExceptionHandler); DISALLOW_COPY_AND_ASSIGN(ClassLinker); }; diff --git a/src/dex_file.h b/src/dex_file.h index ee5cc45c30..51f5d08a90 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -244,7 +244,7 @@ class DexFile { remaining_count_ = -1; } - bool End() const { + bool HasNext() const { return remaining_count_ == -1 && catch_all_ == false; } diff --git a/src/exception.h b/src/exception.h new file mode 100644 index 0000000000..5b31e86449 --- /dev/null +++ b/src/exception.h @@ -0,0 +1,22 @@ +// 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 a9673effae..99fd9548d3 100644 --- a/src/exception_test.cc +++ b/src/exception_test.cc @@ -61,19 +61,46 @@ static const char kMyClassExceptionHandleDex[] = "IAAAAgAAAAkDAAAAEAAAAQAAACgDAAA="; class ExceptionTest : public CommonTest { -}; + protected: + virtual void SetUp() { + CommonTest::SetUp(); + + dex_.reset(OpenDexFileBase64(kMyClassExceptionHandleDex, "kMyClassExceptionHandleDex")); + ASSERT_TRUE(dex_ != NULL); + PathClassLoader* class_loader = AllocPathClassLoader(dex_.get()); + ASSERT_TRUE(class_loader != NULL); + my_klass_ = class_linker_->FindClass("Ljava/lang/MyClass;", class_loader); + ASSERT_TRUE(my_klass_ != NULL); + method_f_ = my_klass_->FindVirtualMethod("f", "()I"); + ASSERT_TRUE(method_f_ != NULL); + method_g_ = my_klass_->FindVirtualMethod("g", "(I)V"); + ASSERT_TRUE(method_g_ != NULL); + } + + DexFile::CatchHandlerItem FindCatchHandlerItem(Method* method, + const char exception_type[], + uint32_t addr) { + const DexFile::CodeItem* code_item = dex_->GetCodeItem(method->code_off_); + for (DexFile::CatchHandlerIterator iter = dex_->dexFindCatchHandler(*code_item, addr); + !iter.HasNext(); iter.Next()) { + if (strcmp(exception_type, dex_->dexStringByTypeIdx(iter.Get().type_idx_)) == 0) { + return iter.Get(); + } + } + return DexFile::CatchHandlerItem(); + } -TEST_F(ExceptionTest, MyClass_F_G) { - scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassExceptionHandleDex, "kMyClassExceptionHandleDex")); - PathClassLoader* class_loader = AllocPathClassLoader(dex.get()); - Class* klass = class_linker_->FindClass("Ljava/lang/MyClass;", class_loader); - ASSERT_TRUE(klass != NULL); + scoped_ptr<DexFile> dex_; - Method* method_f = klass->FindVirtualMethod("f", "()I"); - ASSERT_TRUE(method_f != NULL); + Method* method_f_; + Method* method_g_; - const DexFile& dex_file = class_linker_->FindDexFile(klass->GetDexCache()); - const DexFile::CodeItem *code_item = dex_file.GetCodeItem(method_f->code_off_); + private: + Class* my_klass_; +}; + +TEST_F(ExceptionTest, FindCatchHandler) { + const DexFile::CodeItem *code_item = dex_->GetCodeItem(method_f_->code_off_); ASSERT_TRUE(code_item != NULL); @@ -81,25 +108,28 @@ TEST_F(ExceptionTest, MyClass_F_G) { ASSERT_NE(0u, code_item->insns_size_); const struct DexFile::TryItem *t0, *t1; - t0 = dex_file.dexGetTryItems(*code_item, 0); - t1 = dex_file.dexGetTryItems(*code_item, 1); + t0 = dex_->dexGetTryItems(*code_item, 0); + t1 = dex_->dexGetTryItems(*code_item, 1); EXPECT_LE(t0->start_addr_, t1->start_addr_); DexFile::CatchHandlerIterator iter = - dex_file.dexFindCatchHandler(*code_item, 4 /* Dex PC in the first try block */); - ASSERT_EQ(false, iter.End()); - EXPECT_STREQ("Ljava/io/IOException;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_)); + dex_->dexFindCatchHandler(*code_item, 4 /* Dex PC in the first try block */); + ASSERT_EQ(false, iter.HasNext()); + EXPECT_STREQ("Ljava/io/IOException;", dex_->dexStringByTypeIdx(iter.Get().type_idx_)); iter.Next(); - ASSERT_EQ(false, iter.End()); - EXPECT_STREQ("Ljava/lang/Exception;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_)); + ASSERT_EQ(false, iter.HasNext()); + EXPECT_STREQ("Ljava/lang/Exception;", dex_->dexStringByTypeIdx(iter.Get().type_idx_)); iter.Next(); - ASSERT_EQ(true, iter.End()); + ASSERT_EQ(true, iter.HasNext()); - iter = dex_file.dexFindCatchHandler(*code_item, 8 /* Dex PC in the second try block */); - ASSERT_EQ(false, iter.End()); - EXPECT_STREQ("Ljava/io/IOException;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_)); + iter = dex_->dexFindCatchHandler(*code_item, 8 /* Dex PC in the second try block */); + ASSERT_EQ(false, iter.HasNext()); + EXPECT_STREQ("Ljava/io/IOException;", dex_->dexStringByTypeIdx(iter.Get().type_idx_)); iter.Next(); - ASSERT_EQ(true, iter.End()); + ASSERT_EQ(true, iter.HasNext()); + + iter = dex_->dexFindCatchHandler(*code_item, 11 /* Dex PC not in any try block */); + ASSERT_EQ(true, iter.HasNext()); } } // namespace art diff --git a/src/object.h b/src/object.h index 840c9665d9..5a8f5be160 100644 --- a/src/object.h +++ b/src/object.h @@ -485,6 +485,18 @@ class Method : public AccessibleObject { // arguments into an array of arguments. size_t NumArgArrayBytes() const; + // Converts a native PC to a virtual PC. TODO: this is a no-op + // until we associate a PC mapping table with each method. + uintptr_t ToDexPC(const uintptr_t pc) const { + return pc; + } + + // Converts a virtual PC to a native PC. TODO: this is a no-op + // until we associate a PC mapping table with each method. + uintptr_t ToNativePC(const uintptr_t pc) const { + return pc; + } + public: // TODO: private // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // the class we are a part of @@ -573,7 +585,9 @@ class Method : public AccessibleObject { return code_ != NULL; } - void SetCode(const byte* compiled_code, size_t byte_count, InstructionSet set) { + void SetCode(const byte* compiled_code, + size_t byte_count, + InstructionSet set) { // Copy the code into an executable region. code_instruction_set_ = set; code_area_.reset(MemMap::Map(byte_count, @@ -595,6 +609,18 @@ class Method : public AccessibleObject { frame_size_ = frame_size; } + void SetPcOffset(uint32_t pc_offset) { + pc_offset_ = pc_offset; + } + + size_t GetFrameSize() const { + return frame_size_; + } + + size_t GetPcOffset() const { + return pc_offset_; + } + void SetCoreSpillMask(uint32_t core_spill_mask) { core_spill_mask_ = core_spill_mask; } @@ -692,6 +718,9 @@ class Method : public AccessibleObject { // Size in bytes of compiled code associated with this method const uint32_t code_size_; + // Offset of PC within compiled code (in bytes) + uint32_t pc_offset_; + // Any native method registered with this method const void* native_method_; diff --git a/src/thread.cc b/src/thread.cc index e92f23c253..db4c59f95b 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -49,6 +49,24 @@ void Mutex::Unlock() { SetOwner(NULL); } +void Frame::Next() { + byte* next_sp = reinterpret_cast<byte*>(sp_) + + GetMethod()->GetFrameSize(); + sp_ = reinterpret_cast<const Method**>(next_sp); +} + +void* Frame::GetPC() const { + byte* pc_addr = reinterpret_cast<byte*>(sp_) + + GetMethod()->GetPcOffset(); + return reinterpret_cast<void*>(pc_addr); +} + +const Method* Frame::NextMethod() const { + byte* next_sp = reinterpret_cast<byte*>(sp_) + + GetMethod()->GetFrameSize(); + return reinterpret_cast<const Method*>(next_sp); +} + void* ThreadStart(void *arg) { UNIMPLEMENTED(FATAL); return NULL; @@ -190,6 +208,67 @@ void Thread::ThrowNewException(const char* exception_class_name, const char* fmt ThrowNewException(exception_class, msg.c_str()); } +Frame Thread::FindExceptionHandler(void* throw_pc, void** handler_pc) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + DCHECK(class_linker != NULL); + + Frame cur_frame = GetTopOfStack(); + for (int unwind_depth = 0; ; unwind_depth++) { + const Method* cur_method = cur_frame.GetMethod(); + DexCache* dex_cache = cur_method->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = class_linker->FindDexFile(dex_cache); + + void* handler_addr = FindExceptionHandlerInMethod(cur_method, + throw_pc, + dex_file, + class_linker); + if (handler_addr) { + *handler_pc = handler_addr; + return cur_frame; + } else { + // Check if we are at the last frame + if (cur_frame.HasNext()) { + cur_frame.Next(); + } else { + // Either at the top of stack or next frame is native. + break; + } + } + } + *handler_pc = NULL; + return Frame(); +} + +void* Thread::FindExceptionHandlerInMethod(const Method* method, + void* throw_pc, + const DexFile& dex_file, + ClassLinker* class_linker) { + Object* exception_obj = exception_; + exception_ = NULL; + + intptr_t dex_pc = -1; + const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->code_off_); + DexFile::CatchHandlerIterator iter; + for (iter = dex_file.dexFindCatchHandler(*code_item, + method->ToDexPC(reinterpret_cast<intptr_t>(throw_pc))); + !iter.HasNext(); + iter.Next()) { + Class* klass = class_linker->FindSystemClass(dex_file.dexStringByTypeIdx(iter.Get().type_idx_)); + DCHECK(klass != NULL); + if (exception_obj->InstanceOf(klass)) { + dex_pc = iter.Get().address_; + break; + } + } + + exception_ = exception_obj; + if (iter.HasNext()) { + return NULL; + } else { + return reinterpret_cast<void*>( method->ToNativePC(dex_pc) ); + } +} + static const char* kStateNames[] = { "New", "Runnable", diff --git a/src/thread.h b/src/thread.h index 6677f7c91d..b6fad0f7dd 100644 --- a/src/thread.h +++ b/src/thread.h @@ -113,6 +113,40 @@ struct NativeToManagedRecord { void* last_top_of_managed_stack; }; +// Iterator over managed frames up to the first native-to-managed transition +class Frame { + Frame() : sp_(NULL) {} + + const Method* GetMethod() const { + return *sp_; + } + + bool HasNext() const { + return NextMethod() != NULL; + } + + void Next(); + + void* GetPC() const; + + const Method** GetSP() const { + return sp_; + } + + // TODO: this is here for testing, remove when we have exception unit tests + // that use the real stack + void SetSP(const Method** sp) { + sp_ = sp; + } + + private: + const Method* NextMethod() const; + + friend class Thread; + + const Method** sp_; +}; + class Thread { public: enum State { @@ -202,6 +236,16 @@ class Thread { } // TODO: Throwable* + Frame GetTopOfStack() const { + return top_of_managed_stack_; + } + + // TODO: this is here for testing, remove when we have exception unit tests + // that use the real stack + void SetTopOfStack(void* stack) { + top_of_managed_stack_.SetSP(reinterpret_cast<const Method**>(stack)); + } + void SetException(Object* new_exception) { CHECK(new_exception != NULL); // TODO: CHECK(exception_ == NULL); @@ -217,6 +261,13 @@ class Thread { exception_ = NULL; } + Frame FindExceptionHandler(void* throw_pc, void** handler_pc); + + void* FindExceptionHandlerInMethod(const Method* method, + void* throw_pc, + const DexFile& dex_file, + ClassLinker* class_linker); + // Offset of exception within Thread, used by generated code static ThreadOffset ExceptionOffset() { return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_)); @@ -242,10 +293,6 @@ class Thread { static bool Init(); - Runtime* GetRuntime() const { - return runtime_; - } - State GetState() const { return state_; } @@ -275,7 +322,8 @@ class Thread { // Offset of top of managed stack address, used by generated code static ThreadOffset TopOfManagedStackOffset() { - return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_)); + return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_) + + OFFSETOF_MEMBER(Frame, sp_)); } // Offset of top stack handle block within Thread, used by generated code @@ -313,14 +361,14 @@ class Thread { // Linked list recording transitions from native to managed code void PushNativeToManagedRecord(NativeToManagedRecord* record) { - record->last_top_of_managed_stack = top_of_managed_stack_; + record->last_top_of_managed_stack = reinterpret_cast<void*>(top_of_managed_stack_.GetSP()); record->link = native_to_managed_record_; native_to_managed_record_ = record; - top_of_managed_stack_ = NULL; + top_of_managed_stack_.SetSP(NULL); } void PopNativeToManagedRecord(const NativeToManagedRecord& record) { native_to_managed_record_ = record.link; - top_of_managed_stack_ = record.last_top_of_managed_stack; + top_of_managed_stack_.SetSP( reinterpret_cast<const Method**>(record.last_top_of_managed_stack) ); } ClassLoader* GetClassLoaderOverride() { @@ -334,7 +382,7 @@ class Thread { private: Thread() : id_(1234), - top_of_managed_stack_(NULL), + top_of_managed_stack_(), native_to_managed_record_(NULL), top_shb_(NULL), jni_env_(NULL), @@ -358,7 +406,7 @@ class Thread { // Top of the managed stack, written out prior to the state transition from // kRunnable to kNative. Uses include to give the starting point for scanning // a managed stack when a thread is in native code. - void* top_of_managed_stack_; + Frame top_of_managed_stack_; // A linked list (of stack allocated records) recording transitions from // native to managed code. |