diff options
author | Ian Rogers <irogers@google.com> | 2014-05-29 21:31:50 -0700 |
---|---|---|
committer | Ian Rogers <irogers@google.com> | 2014-05-30 12:46:10 -0700 |
commit | 5cf98196d488437acd1e989c08a554ef697fded1 (patch) | |
tree | dd44bc0120562169b701e80dbec413a179862beb /runtime/quick_exception_handler.cc | |
parent | b7f02280f7f56ae94fe7f01e161be0b725b6e4a9 (diff) | |
download | android_art-5cf98196d488437acd1e989c08a554ef697fded1.tar.gz android_art-5cf98196d488437acd1e989c08a554ef697fded1.tar.bz2 android_art-5cf98196d488437acd1e989c08a554ef697fded1.zip |
Don't report down-calls as unhandled exceptions.
Bug: 15310540
Also, narrow scope of catch/deoptimize stack visitors that are specific to
quick exception delivery.
Change-Id: Ib13a006ce1347acb93a36b0186550d4c3ec2034b
Diffstat (limited to 'runtime/quick_exception_handler.cc')
-rw-r--r-- | runtime/quick_exception_handler.cc | 186 |
1 files changed, 174 insertions, 12 deletions
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 243818c4a2..b9cec40ebf 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -16,25 +16,105 @@ #include "quick_exception_handler.h" -#include "catch_block_stack_visitor.h" -#include "deoptimize_stack_visitor.h" +#include "dex_instruction.h" #include "entrypoints/entrypoint_utils.h" -#include "mirror/art_method-inl.h" #include "handle_scope-inl.h" +#include "mirror/art_method-inl.h" +#include "verifier/method_verifier.h" namespace art { +static constexpr bool kDebugExceptionDelivery = false; +static constexpr size_t kInvalidFrameId = 0xffffffff; + QuickExceptionHandler::QuickExceptionHandler(Thread* self, bool is_deoptimization) : self_(self), context_(self->GetLongJumpContext()), is_deoptimization_(is_deoptimization), method_tracing_active_(is_deoptimization || Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()), - handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_dex_pc_(0), - clear_exception_(false), handler_frame_id_(kInvalidFrameId) { + handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_method_(nullptr), + handler_dex_pc_(0), clear_exception_(false), handler_frame_id_(kInvalidFrameId) { } +// Finds catch handler or prepares for deoptimization. +class CatchBlockStackVisitor FINAL : public StackVisitor { + public: + CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception, + QuickExceptionHandler* exception_handler) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(self, context), self_(self), exception_(exception), + exception_handler_(exception_handler) { + } + + bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* method = GetMethod(); + exception_handler_->SetHandlerFrameId(GetFrameId()); + if (method == nullptr) { + // This is the upcall, we remember the frame and last pc so that we may long jump to them. + exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc()); + exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame()); + uint32_t next_dex_pc; + mirror::ArtMethod* next_art_method; + bool has_next = GetNextMethodAndDexPc(&next_art_method, &next_dex_pc); + // Report the method that did the down call as the handler. + exception_handler_->SetHandlerDexPc(next_dex_pc); + exception_handler_->SetHandlerMethod(next_art_method); + if (!has_next) { + // No next method? Check exception handler is set up for the unhandled exception handler + // case. + DCHECK_EQ(0U, exception_handler_->GetHandlerDexPc()); + DCHECK(nullptr == exception_handler_->GetHandlerMethod()); + } + return false; // End stack walk. + } + if (method->IsRuntimeMethod()) { + // Ignore callee save method. + DCHECK(method->IsCalleeSaveMethod()); + return true; + } + return HandleTryItems(method); + } + + private: + bool HandleTryItems(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint32_t dex_pc = DexFile::kDexNoIndex; + if (!method->IsNative()) { + dex_pc = GetDexPc(); + } + if (dex_pc != DexFile::kDexNoIndex) { + bool clear_exception = false; + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> to_find(hs.NewHandle((*exception_)->GetClass())); + uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception); + exception_handler_->SetClearException(clear_exception); + if (found_dex_pc != DexFile::kDexNoIndex) { + exception_handler_->SetHandlerMethod(method); + exception_handler_->SetHandlerDexPc(found_dex_pc); + exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc)); + exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame()); + return false; // End stack walk. + } + } + return true; // Continue stack walk. + } + + Thread* const self_; + // The exception we're looking for the catch block of. + Handle<mirror::Throwable>* exception_; + // The quick exception handler we're visiting for. + QuickExceptionHandler* const exception_handler_; + + DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor); +}; + void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception) { DCHECK(!is_deoptimization_); + if (kDebugExceptionDelivery) { + mirror::String* msg = exception->GetDetailMessage(); + std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : ""); + self_->DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) + << ": " << str_msg << "\n"); + } StackHandleScope<1> hs(self_); Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception)); @@ -42,14 +122,14 @@ void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location, CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this); visitor.WalkStack(true); - mirror::ArtMethod* catch_method = handler_quick_frame_->AsMirrorPtr(); if (kDebugExceptionDelivery) { - if (catch_method == nullptr) { + if (handler_quick_frame_->AsMirrorPtr() == nullptr) { LOG(INFO) << "Handler is upcall"; - } else { - const DexFile& dex_file = *catch_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); - int line_number = dex_file.GetLineNumFromPC(catch_method, handler_dex_pc_); - LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")"; + } + if (handler_method_ != nullptr) { + const DexFile& dex_file = *handler_method_->GetDeclaringClass()->GetDexCache()->GetDexFile(); + int line_number = dex_file.GetLineNumFromPC(handler_method_, handler_dex_pc_); + LOG(INFO) << "Handler: " << PrettyMethod(handler_method_) << " (line: " << line_number << ")"; } } if (clear_exception_) { @@ -62,12 +142,94 @@ void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location, // The debugger may suspend this thread and walk its stack. Let's do this before popping // instrumentation frames. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - instrumentation->ExceptionCaughtEvent(self_, throw_location, catch_method, handler_dex_pc_, + instrumentation->ExceptionCaughtEvent(self_, throw_location, handler_method_, handler_dex_pc_, exception_ref.Get()); } +// Prepares deoptimization. +class DeoptimizeStackVisitor FINAL : public StackVisitor { + public: + DeoptimizeStackVisitor(Thread* self, Context* context, QuickExceptionHandler* exception_handler) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(self, context), self_(self), exception_handler_(exception_handler), + prev_shadow_frame_(nullptr) { + CHECK(!self_->HasDeoptimizationShadowFrame()); + } + + bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + exception_handler_->SetHandlerFrameId(GetFrameId()); + mirror::ArtMethod* method = GetMethod(); + if (method == nullptr) { + // This is the upcall, we remember the frame and last pc so that we may long jump to them. + exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc()); + exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame()); + return false; // End stack walk. + } else if (method->IsRuntimeMethod()) { + // Ignore callee save method. + DCHECK(method->IsCalleeSaveMethod()); + return true; + } else { + return HandleDeoptimization(method); + } + } + + private: + bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + MethodHelper mh(m); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + CHECK(code_item != nullptr); + uint16_t num_regs = code_item->registers_size_; + uint32_t dex_pc = GetDexPc(); + const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); + uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits(); + ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc); + StackHandleScope<2> hs(self_); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(mh.GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(mh.GetClassLoader())); + verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, + &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m, + m->GetAccessFlags(), false, true, true); + verifier.Verify(); + std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc); + for (uint16_t reg = 0; reg < num_regs; ++reg) { + VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2)); + switch (kind) { + case kUndefined: + new_frame->SetVReg(reg, 0xEBADDE09); + break; + case kConstant: + new_frame->SetVReg(reg, kinds.at((reg * 2) + 1)); + break; + case kReferenceVReg: + new_frame->SetVRegReference(reg, + reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind))); + break; + default: + new_frame->SetVReg(reg, GetVReg(m, reg, kind)); + break; + } + } + if (prev_shadow_frame_ != nullptr) { + prev_shadow_frame_->SetLink(new_frame); + } else { + self_->SetDeoptimizationShadowFrame(new_frame); + } + prev_shadow_frame_ = new_frame; + return true; + } + + Thread* const self_; + QuickExceptionHandler* const exception_handler_; + ShadowFrame* prev_shadow_frame_; + + DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor); +}; + void QuickExceptionHandler::DeoptimizeStack() { DCHECK(is_deoptimization_); + if (kDebugExceptionDelivery) { + self_->DumpStack(LOG(INFO) << "Deoptimizing: "); + } DeoptimizeStackVisitor visitor(self_, context_, this); visitor.WalkStack(true); |