From fd3077e4b9ebadd281777310d26e64443858f653 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 23 Apr 2014 10:32:43 +0200 Subject: Refactor exception handling for deoptimization This CL refactors the exception handling (on the quick side) by isolating the search of catch handler and the preparation of deoptimization. We rename the CatchFinder class to QuickExceptionHandler so it's less specific to catch handler search. Finding catch handler happens in QuickExceptionHandler::FindCatch. Since the CatchBlockStackVisitor resolves exception types, it may cause thread suspension and breaks the assertion current thread can't be suspended. Therefore, we place the exception in a SirtRef (while it is detached from the current thread) and remove the thread suspension assertion. Deoptimization now happens in QuickExceptionHandler::DeoptimizeStack. It uses the new DeoptimizeStackVisitor class to create shadow frames. We also add the Thread::GetDeoptimizationException method to get the definition of the fake exception in only one place. Change-Id: I01b19fa72af64329b5c3b6c7f0c3339d2d724978 --- runtime/Android.mk | 3 +- runtime/catch_block_stack_visitor.cc | 70 ++-------- runtime/catch_block_stack_visitor.h | 36 +++--- runtime/catch_finder.cc | 135 -------------------- runtime/catch_finder.h | 95 -------------- runtime/deoptimize_stack_visitor.cc | 87 +++++++++++++ runtime/deoptimize_stack_visitor.h | 53 ++++++++ .../quick/quick_deoptimization_entrypoints.cc | 2 +- runtime/mirror/art_method.cc | 6 +- runtime/quick_exception_handler.cc | 141 +++++++++++++++++++++ runtime/quick_exception_handler.h | 100 +++++++++++++++ runtime/thread.cc | 18 ++- runtime/thread.h | 5 + 13 files changed, 432 insertions(+), 319 deletions(-) delete mode 100644 runtime/catch_finder.cc delete mode 100644 runtime/catch_finder.h create mode 100644 runtime/deoptimize_stack_visitor.cc create mode 100644 runtime/deoptimize_stack_visitor.h create mode 100644 runtime/quick_exception_handler.cc create mode 100644 runtime/quick_exception_handler.h diff --git a/runtime/Android.mk b/runtime/Android.mk index d433fd5b86..bc971a9d8d 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -36,10 +36,10 @@ LIBART_COMMON_SRC_FILES := \ base/unix_file/string_file.cc \ check_jni.cc \ catch_block_stack_visitor.cc \ - catch_finder.cc \ class_linker.cc \ common_throws.cc \ debugger.cc \ + deoptimize_stack_visitor.cc \ dex_file.cc \ dex_file_verifier.cc \ dex_instruction.cc \ @@ -129,6 +129,7 @@ LIBART_COMMON_SRC_FILES := \ os_linux.cc \ parsed_options.cc \ primitive.cc \ + quick_exception_handler.cc \ quick/inline_method_analyser.cc \ reference_table.cc \ reflection.cc \ diff --git a/runtime/catch_block_stack_visitor.cc b/runtime/catch_block_stack_visitor.cc index 410fff9f96..8d10a97b68 100644 --- a/runtime/catch_block_stack_visitor.cc +++ b/runtime/catch_block_stack_visitor.cc @@ -17,27 +17,26 @@ #include "catch_block_stack_visitor.h" #include "dex_instruction.h" -#include "catch_finder.h" +#include "mirror/art_method-inl.h" +#include "quick_exception_handler.h" #include "sirt_ref.h" #include "verifier/method_verifier.h" namespace art { bool CatchBlockStackVisitor::VisitFrame() { - catch_finder_->SetHandlerFrameId(GetFrameId()); + 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. - catch_finder_->SetHandlerQuickFramePc(GetCurrentQuickFramePc()); - catch_finder_->SetHandlerQuickFrame(GetCurrentQuickFrame()); + 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 if (is_deoptimization_) { - return HandleDeoptimization(method); } else { return HandleTryItems(method); } @@ -46,68 +45,21 @@ bool CatchBlockStackVisitor::VisitFrame() { bool CatchBlockStackVisitor::HandleTryItems(mirror::ArtMethod* method) { uint32_t dex_pc = DexFile::kDexNoIndex; - if (method->IsNative()) { - ++native_method_count_; - } else { + if (!method->IsNative()) { dex_pc = GetDexPc(); } if (dex_pc != DexFile::kDexNoIndex) { bool clear_exception = false; - SirtRef sirt_method_to_find(Thread::Current(), to_find_); - uint32_t found_dex_pc = method->FindCatchBlock(sirt_method_to_find, dex_pc, &clear_exception); - to_find_ = sirt_method_to_find.get(); - catch_finder_->SetClearException(clear_exception); + uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc, &clear_exception); + exception_handler_->SetClearException(clear_exception); if (found_dex_pc != DexFile::kDexNoIndex) { - catch_finder_->SetHandlerDexPc(found_dex_pc); - catch_finder_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc)); - catch_finder_->SetHandlerQuickFrame(GetCurrentQuickFrame()); + 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. } -bool CatchBlockStackVisitor::HandleDeoptimization(mirror::ArtMethod* m) { - 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); - SirtRef dex_cache(self_, mh.GetDexCache()); - SirtRef class_loader(self_, mh.GetClassLoader()); - verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, - &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m, - m->GetAccessFlags(), false, true); - verifier.Verify(); - std::vector kinds = verifier.DescribeVRegs(dex_pc); - for (uint16_t reg = 0; reg < num_regs; ++reg) { - VRegKind kind = static_cast(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(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 { - catch_finder_->SetTopShadowFrame(new_frame); - } - prev_shadow_frame_ = new_frame; - return true; -} - } // namespace art diff --git a/runtime/catch_block_stack_visitor.h b/runtime/catch_block_stack_visitor.h index ce67e276b9..6f0fe11e75 100644 --- a/runtime/catch_block_stack_visitor.h +++ b/runtime/catch_block_stack_visitor.h @@ -17,39 +17,39 @@ #ifndef ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_ #define ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_ -#include "mirror/throwable.h" -#include "thread.h" +#include "mirror/object-inl.h" +#include "stack.h" +#include "sirt_ref-inl.h" namespace art { -class CatchFinder; + +namespace mirror { +class Throwable; +} // namespace mirror +class Context; +class QuickExceptionHandler; +class Thread; class ThrowLocation; // Finds catch handler or prepares deoptimization. -class CatchBlockStackVisitor : public StackVisitor { +class CatchBlockStackVisitor FINAL : public StackVisitor { public: - CatchBlockStackVisitor(Thread* self, Context* context, mirror::Throwable* exception, - bool is_deoptimization, CatchFinder* catch_finder) + CatchBlockStackVisitor(Thread* self, Context* context, SirtRef& exception, + QuickExceptionHandler* exception_handler) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(self, context), - self_(self), is_deoptimization_(is_deoptimization), - to_find_(is_deoptimization ? nullptr : exception->GetClass()), - catch_finder_(catch_finder), native_method_count_(0), prev_shadow_frame_(nullptr) { + : StackVisitor(self, context), self_(self), to_find_(self, exception->GetClass()), + exception_handler_(exception_handler) { } - bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: bool HandleTryItems(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Thread* const self_; - const bool is_deoptimization_; // The type of the exception catch block to find. - mirror::Class* to_find_; - CatchFinder* const catch_finder_; - // Number of native methods passed in crawl (equates to number of SIRTs to pop) - uint32_t native_method_count_; - ShadowFrame* prev_shadow_frame_; + SirtRef to_find_; + QuickExceptionHandler* const exception_handler_; DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor); }; diff --git a/runtime/catch_finder.cc b/runtime/catch_finder.cc deleted file mode 100644 index f0293d738f..0000000000 --- a/runtime/catch_finder.cc +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "catch_finder.h" -#include "catch_block_stack_visitor.h" - -namespace art { - -CatchFinder::CatchFinder(Thread* self, const ThrowLocation& throw_location, - mirror::Throwable* exception, bool is_deoptimization) - : self_(self), context_(self->GetLongJumpContext()), - exception_(exception), is_deoptimization_(is_deoptimization), throw_location_(throw_location), - 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), top_shadow_frame_(nullptr), - handler_frame_id_(kInvalidFrameId) { - // Exception not in root sets, can't allow GC. - last_no_assert_suspension_cause_ = self->StartAssertNoThreadSuspension("Finding catch block"); -} - -void CatchFinder::FindCatch() { - // Walk the stack to find catch handler or prepare for deoptimization. - CatchBlockStackVisitor visitor(self_, context_, exception_, is_deoptimization_, this); - visitor.WalkStack(true); - - mirror::ArtMethod* catch_method = *handler_quick_frame_; - if (catch_method == nullptr) { - if (kDebugExceptionDelivery) { - LOG(INFO) << "Handler is upcall"; - } - } else { - CHECK(!is_deoptimization_); - if (kDebugExceptionDelivery) { - 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 (clear_exception_) { - // Exception was cleared as part of delivery. - DCHECK(!self_->IsExceptionPending()); - } else { - // Put exception back in root set with clear throw location. - self_->SetException(ThrowLocation(), exception_); - } - self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_); - // Do instrumentation events after allowing thread suspension again. - if (!is_deoptimization_) { - // 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_, - exception_); - } -} - -// Unwinds all instrumentation stack frame prior to catch handler or upcall. -class InstrumentationStackVisitor : public StackVisitor { - public: - InstrumentationStackVisitor(Thread* self, bool is_deoptimization, size_t frame_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(self, nullptr), - self_(self), frame_id_(frame_id), - instrumentation_frames_to_pop_(0) { - CHECK_NE(frame_id_, kInvalidFrameId); - } - - bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - size_t current_frame_id = GetFrameId(); - if (current_frame_id > frame_id_) { - CHECK(GetMethod() != nullptr); - if (UNLIKELY(GetQuickInstrumentationExitPc() == GetReturnPc())) { - ++instrumentation_frames_to_pop_; - } - return true; - } else { - // We reached the frame of the catch handler or the upcall. - return false; - } - } - - size_t GetInstrumentationFramesToPop() const { - return instrumentation_frames_to_pop_; - } - - private: - Thread* const self_; - const size_t frame_id_; - size_t instrumentation_frames_to_pop_; - - DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor); -}; - -void CatchFinder::UpdateInstrumentationStack() { - if (method_tracing_active_) { - InstrumentationStackVisitor visitor(self_, is_deoptimization_, handler_frame_id_); - visitor.WalkStack(true); - - size_t instrumentation_frames_to_pop = visitor.GetInstrumentationFramesToPop(); - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - for (size_t i = 0; i < instrumentation_frames_to_pop; ++i) { - instrumentation->PopMethodForUnwind(self_, is_deoptimization_); - } - } -} - -void CatchFinder::DoLongJump() { - if (is_deoptimization_) { - // TODO: proper return value. - self_->SetDeoptimizationShadowFrame(top_shadow_frame_); - } - // Place context back on thread so it will be available when we continue. - self_->ReleaseLongJumpContext(context_); - context_->SetSP(reinterpret_cast(handler_quick_frame_)); - CHECK_NE(handler_quick_frame_pc_, 0u); - context_->SetPC(handler_quick_frame_pc_); - context_->SmashCallerSaves(); - context_->DoLongJump(); -} - -} // namespace art diff --git a/runtime/catch_finder.h b/runtime/catch_finder.h deleted file mode 100644 index ebbafe2580..0000000000 --- a/runtime/catch_finder.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_CATCH_FINDER_H_ -#define ART_RUNTIME_CATCH_FINDER_H_ - -#include "mirror/art_method-inl.h" -#include "thread.h" - -namespace art { - -static constexpr bool kDebugExceptionDelivery = false; -static constexpr size_t kInvalidFrameId = 0xffffffff; - -// Manages exception delivery for Quick backend. Not used by Portable backend. -class CatchFinder { - public: - CatchFinder(Thread* self, const ThrowLocation& throw_location, mirror::Throwable* exception, - bool is_deoptimization) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - ~CatchFinder() { - LOG(FATAL) << "UNREACHABLE"; // Expected to take long jump. - } - - void FindCatch() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void UpdateInstrumentationStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - void SetHandlerQuickFrame(mirror::ArtMethod** handler_quick_frame) { - handler_quick_frame_ = handler_quick_frame; - } - - void SetHandlerQuickFramePc(uintptr_t handler_quick_frame_pc) { - handler_quick_frame_pc_ = handler_quick_frame_pc; - } - - void SetHandlerDexPc(uint32_t dex_pc) { - handler_dex_pc_ = dex_pc; - } - - void SetClearException(bool clear_exception) { - clear_exception_ = clear_exception; - } - - void SetTopShadowFrame(ShadowFrame* top_shadow_frame) { - top_shadow_frame_ = top_shadow_frame; - } - - void SetHandlerFrameId(size_t frame_id) { - handler_frame_id_ = frame_id; - } - - private: - Thread* const self_; - Context* const context_; - mirror::Throwable* const exception_; - const bool is_deoptimization_; - // Location of the throw. - const ThrowLocation& throw_location_; - // Is method tracing active? - const bool method_tracing_active_; - // Support for nesting no thread suspension checks. - const char* last_no_assert_suspension_cause_; - // Quick frame with found handler or last frame if no handler found. - mirror::ArtMethod** handler_quick_frame_; - // PC to branch to for the handler. - uintptr_t handler_quick_frame_pc_; - // Associated dex PC. - uint32_t handler_dex_pc_; - // Should the exception be cleared as the catch block has no move-exception? - bool clear_exception_; - // Deoptimization top shadow frame. - ShadowFrame* top_shadow_frame_; - // Frame id of the catch handler or the upcall. - size_t handler_frame_id_; - - DISALLOW_COPY_AND_ASSIGN(CatchFinder); -}; - -} // namespace art -#endif // ART_RUNTIME_CATCH_FINDER_H_ diff --git a/runtime/deoptimize_stack_visitor.cc b/runtime/deoptimize_stack_visitor.cc new file mode 100644 index 0000000000..f2eaf00856 --- /dev/null +++ b/runtime/deoptimize_stack_visitor.cc @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "deoptimize_stack_visitor.h" + +#include "mirror/art_method-inl.h" +#include "object_utils.h" +#include "quick_exception_handler.h" +#include "sirt_ref-inl.h" +#include "verifier/method_verifier.h" + +namespace art { + +bool DeoptimizeStackVisitor::VisitFrame() { + 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); + } +} + +bool DeoptimizeStackVisitor::HandleDeoptimization(mirror::ArtMethod* m) { + 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); + SirtRef dex_cache(self_, mh.GetDexCache()); + SirtRef class_loader(self_, mh.GetClassLoader()); + verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, + &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m, + m->GetAccessFlags(), false, true); + verifier.Verify(); + std::vector kinds = verifier.DescribeVRegs(dex_pc); + for (uint16_t reg = 0; reg < num_regs; ++reg) { + VRegKind kind = static_cast(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(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 { + exception_handler_->SetTopShadowFrame(new_frame); + } + prev_shadow_frame_ = new_frame; + return true; +} + +} // namespace art diff --git a/runtime/deoptimize_stack_visitor.h b/runtime/deoptimize_stack_visitor.h new file mode 100644 index 0000000000..c898e7db77 --- /dev/null +++ b/runtime/deoptimize_stack_visitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_ +#define ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_ + +#include "base/mutex.h" +#include "stack.h" + +namespace art { + +namespace mirror { +class ArtMethod; +} // namespace mirror +class QuickExceptionHandler; +class Thread; + +// 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) { + } + + bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + Thread* const self_; + QuickExceptionHandler* const exception_handler_; + ShadowFrame* prev_shadow_frame_; + + DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor); +}; + +} // namespace art +#endif // ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_ diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc index 51c647adf1..6448045e27 100644 --- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc @@ -31,7 +31,7 @@ namespace art { extern "C" void artDeoptimize(Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - self->SetException(ThrowLocation(), reinterpret_cast(-1)); + self->SetException(ThrowLocation(), Thread::GetDeoptimizationException()); self->QuickDeliverException(); } diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index f3303a8267..726004bfee 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -315,13 +315,13 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* } else { (*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]); } - if (UNLIKELY(reinterpret_cast(self->GetException(NULL)) == -1)) { - // Unusual case where we were running LLVM generated code and an + if (UNLIKELY(self->GetException(nullptr) == Thread::GetDeoptimizationException())) { + // Unusual case where we were running generated code and an // exception was thrown to force the activations to be removed from the // stack. Continue execution in the interpreter. self->ClearException(); ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result); - self->SetTopOfStack(NULL, 0); + self->SetTopOfStack(nullptr, 0); self->SetTopOfShadowStack(shadow_frame); interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result); } diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc new file mode 100644 index 0000000000..d5844b6e83 --- /dev/null +++ b/runtime/quick_exception_handler.cc @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "quick_exception_handler.h" + +#include "catch_block_stack_visitor.h" +#include "deoptimize_stack_visitor.h" +#include "entrypoints/entrypoint_utils.h" +#include "sirt_ref-inl.h" + +namespace art { + +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), top_shadow_frame_(nullptr), handler_frame_id_(kInvalidFrameId) { +} + +void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location, + mirror::Throwable* exception) { + DCHECK(!is_deoptimization_); + SirtRef exception_ref(self_, exception); + + // Walk the stack to find catch handler or prepare for deoptimization. + CatchBlockStackVisitor visitor(self_, context_, exception_ref, this); + visitor.WalkStack(true); + + mirror::ArtMethod* catch_method = *handler_quick_frame_; + if (kDebugExceptionDelivery) { + if (catch_method == 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 (clear_exception_) { + // Exception was cleared as part of delivery. + DCHECK(!self_->IsExceptionPending()); + } else { + // Put exception back in root set with clear throw location. + self_->SetException(ThrowLocation(), exception_ref.get()); + } + // 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_, + exception_ref.get()); +} + +void QuickExceptionHandler::DeoptimizeStack() { + DCHECK(is_deoptimization_); + + DeoptimizeStackVisitor visitor(self_, context_, this); + visitor.WalkStack(true); + + // Restore deoptimization exception + self_->SetException(ThrowLocation(), Thread::GetDeoptimizationException()); +} + +// Unwinds all instrumentation stack frame prior to catch handler or upcall. +class InstrumentationStackVisitor : public StackVisitor { + public: + InstrumentationStackVisitor(Thread* self, bool is_deoptimization, size_t frame_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(self, nullptr), + self_(self), frame_id_(frame_id), + instrumentation_frames_to_pop_(0) { + CHECK_NE(frame_id_, kInvalidFrameId); + } + + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + size_t current_frame_id = GetFrameId(); + if (current_frame_id > frame_id_) { + CHECK(GetMethod() != nullptr); + if (UNLIKELY(GetQuickInstrumentationExitPc() == GetReturnPc())) { + ++instrumentation_frames_to_pop_; + } + return true; + } else { + // We reached the frame of the catch handler or the upcall. + return false; + } + } + + size_t GetInstrumentationFramesToPop() const { + return instrumentation_frames_to_pop_; + } + + private: + Thread* const self_; + const size_t frame_id_; + size_t instrumentation_frames_to_pop_; + + DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor); +}; + +void QuickExceptionHandler::UpdateInstrumentationStack() { + if (method_tracing_active_) { + InstrumentationStackVisitor visitor(self_, is_deoptimization_, handler_frame_id_); + visitor.WalkStack(true); + + size_t instrumentation_frames_to_pop = visitor.GetInstrumentationFramesToPop(); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (size_t i = 0; i < instrumentation_frames_to_pop; ++i) { + instrumentation->PopMethodForUnwind(self_, is_deoptimization_); + } + } +} + +void QuickExceptionHandler::DoLongJump() { + if (is_deoptimization_) { + // TODO: proper return value. + self_->SetDeoptimizationShadowFrame(top_shadow_frame_); + } + // Place context back on thread so it will be available when we continue. + self_->ReleaseLongJumpContext(context_); + context_->SetSP(reinterpret_cast(handler_quick_frame_)); + CHECK_NE(handler_quick_frame_pc_, 0u); + context_->SetPC(handler_quick_frame_pc_); + context_->SmashCallerSaves(); + context_->DoLongJump(); +} + +} // namespace art diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h new file mode 100644 index 0000000000..d06ce7c632 --- /dev/null +++ b/runtime/quick_exception_handler.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ +#define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ + +#include "base/logging.h" +#include "base/mutex.h" + +namespace art { + +namespace mirror { +class ArtMethod; +class Throwable; +} // namespace mirror +class Context; +class Thread; +class ThrowLocation; +class ShadowFrame; + +static constexpr bool kDebugExceptionDelivery = false; +static constexpr size_t kInvalidFrameId = 0xffffffff; + +// Manages exception delivery for Quick backend. Not used by Portable backend. +class QuickExceptionHandler { + public: + QuickExceptionHandler(Thread* self, bool is_deoptimization) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ~QuickExceptionHandler() { + LOG(FATAL) << "UNREACHABLE"; // Expected to take long jump. + } + + void FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DeoptimizeStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void UpdateInstrumentationStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void SetHandlerQuickFrame(mirror::ArtMethod** handler_quick_frame) { + handler_quick_frame_ = handler_quick_frame; + } + + void SetHandlerQuickFramePc(uintptr_t handler_quick_frame_pc) { + handler_quick_frame_pc_ = handler_quick_frame_pc; + } + + void SetHandlerDexPc(uint32_t dex_pc) { + handler_dex_pc_ = dex_pc; + } + + void SetClearException(bool clear_exception) { + clear_exception_ = clear_exception; + } + + void SetTopShadowFrame(ShadowFrame* top_shadow_frame) { + top_shadow_frame_ = top_shadow_frame; + } + + void SetHandlerFrameId(size_t frame_id) { + handler_frame_id_ = frame_id; + } + + private: + Thread* const self_; + Context* const context_; + const bool is_deoptimization_; + // Is method tracing active? + const bool method_tracing_active_; + // Quick frame with found handler or last frame if no handler found. + mirror::ArtMethod** handler_quick_frame_; + // PC to branch to for the handler. + uintptr_t handler_quick_frame_pc_; + // Associated dex PC. + uint32_t handler_dex_pc_; + // Should the exception be cleared as the catch block has no move-exception? + bool clear_exception_; + // Deoptimization top shadow frame. + ShadowFrame* top_shadow_frame_; + // Frame id of the catch handler or the upcall. + size_t handler_frame_id_; + + DISALLOW_COPY_AND_ASSIGN(QuickExceptionHandler); +}; + +} // namespace art +#endif // ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ diff --git a/runtime/thread.cc b/runtime/thread.cc index 998579dbc7..095404f8c3 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -32,7 +32,6 @@ #include "arch/context.h" #include "base/mutex.h" -#include "catch_finder.h" #include "class_linker.h" #include "class_linker-inl.h" #include "cutils/atomic.h" @@ -54,6 +53,7 @@ #include "mirror/stack_trace_element.h" #include "monitor.h" #include "object_utils.h" +#include "quick_exception_handler.h" #include "reflection.h" #include "runtime.h" #include "scoped_thread_state_change.h" @@ -1841,7 +1841,7 @@ void Thread::QuickDeliverException() { // Don't leave exception visible while we try to find the handler, which may cause class // resolution. ClearException(); - bool is_deoptimization = (exception == reinterpret_cast(-1)); + bool is_deoptimization = (exception == GetDeoptimizationException()); if (kDebugExceptionDelivery) { if (!is_deoptimization) { mirror::String* msg = exception->GetDetailMessage(); @@ -1852,10 +1852,14 @@ void Thread::QuickDeliverException() { DumpStack(LOG(INFO) << "Deoptimizing: "); } } - CatchFinder catch_finder(this, throw_location, exception, is_deoptimization); - catch_finder.FindCatch(); - catch_finder.UpdateInstrumentationStack(); - catch_finder.DoLongJump(); + QuickExceptionHandler exception_handler(this, is_deoptimization); + if (is_deoptimization) { + exception_handler.DeoptimizeStack(); + } else { + exception_handler.FindCatch(throw_location, exception); + } + exception_handler.UpdateInstrumentationStack(); + exception_handler.DoLongJump(); LOG(FATAL) << "UNREACHABLE"; } @@ -2060,7 +2064,7 @@ void Thread::VisitRoots(RootCallback* visitor, void* arg) { if (tlsPtr_.opeer != nullptr) { visitor(&tlsPtr_.opeer, arg, thread_id, kRootThreadObject); } - if (tlsPtr_.exception != nullptr) { + if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) { visitor(reinterpret_cast(&tlsPtr_.exception), arg, thread_id, kRootNativeStack); } tlsPtr_.throw_location.VisitRoots(visitor, arg); diff --git a/runtime/thread.h b/runtime/thread.h index d25bbe997d..e5e4caefce 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -686,6 +686,11 @@ class Thread { return tlsPtr_.single_step_control; } + // Returns the fake exception used to activate deoptimization. + static mirror::Throwable* GetDeoptimizationException() { + return reinterpret_cast(-1); + } + void SetDeoptimizationShadowFrame(ShadowFrame* sf); void SetDeoptimizationReturnValue(const JValue& ret_val); -- cgit v1.2.3-59-g8ed1b