summaryrefslogtreecommitdiff
path: root/runtime/quick_exception_handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/quick_exception_handler.cc')
-rw-r--r--runtime/quick_exception_handler.cc185
1 files changed, 110 insertions, 75 deletions
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index e8d9658dd4..7f5717f736 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -60,18 +60,24 @@ QuickExceptionHandler::QuickExceptionHandler(Thread* self, bool is_deoptimizatio
// Finds catch handler.
class CatchBlockStackVisitor FINAL : public StackVisitor {
public:
- CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,
- QuickExceptionHandler* exception_handler)
+ CatchBlockStackVisitor(Thread* self,
+ Context* context,
+ Handle<mirror::Throwable>* exception,
+ QuickExceptionHandler* exception_handler,
+ uint32_t skip_frames)
REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
exception_(exception),
- exception_handler_(exception_handler) {
+ exception_handler_(exception_handler),
+ skip_frames_(skip_frames) {
}
bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = GetMethod();
exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
if (method == nullptr) {
+ DCHECK_EQ(skip_frames_, 0u)
+ << "We tried to skip an upcall! We should have returned to the upcall to finish delivery";
// 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());
@@ -90,6 +96,10 @@ class CatchBlockStackVisitor FINAL : public StackVisitor {
}
return false; // End stack walk.
}
+ if (skip_frames_ != 0) {
+ skip_frames_--;
+ return true;
+ }
if (method->IsRuntimeMethod()) {
// Ignore callee save method.
DCHECK(method->IsCalleeSaveMethod());
@@ -138,48 +148,115 @@ class CatchBlockStackVisitor FINAL : public StackVisitor {
Handle<mirror::Throwable>* exception_;
// The quick exception handler we're visiting for.
QuickExceptionHandler* const exception_handler_;
+ // The number of frames to skip searching for catches in.
+ uint32_t skip_frames_;
DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
};
+// Counts instrumentation stack frame prior to catch handler or upcall.
+class InstrumentationStackVisitor : public StackVisitor {
+ public:
+ InstrumentationStackVisitor(Thread* self, size_t frame_depth)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ frame_depth_(frame_depth),
+ instrumentation_frames_to_pop_(0) {
+ CHECK_NE(frame_depth_, kInvalidFrameDepth);
+ }
+
+ bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t current_frame_depth = GetFrameDepth();
+ if (current_frame_depth < frame_depth_) {
+ CHECK(GetMethod() != nullptr);
+ if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) {
+ if (!IsInInlinedFrame()) {
+ // We do not count inlined frames, because we do not instrument them. The reason we
+ // include them in the stack walking is the check against `frame_depth_`, which is
+ // given to us by a visitor that visits inlined frames.
+ ++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:
+ const size_t frame_depth_;
+ size_t instrumentation_frames_to_pop_;
+
+ DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor);
+};
+
+// Finds the appropriate exception catch after calling all method exit instrumentation functions.
+// Note that this might change the exception being thrown.
void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
DCHECK(!is_deoptimization_);
+ instrumentation::InstrumentationStackPopper popper(self_);
+ // The number of total frames we have so far popped.
+ uint32_t already_popped = 0;
+ bool popped_to_top = true;
StackHandleScope<1> hs(self_);
- Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
- if (kDebugExceptionDelivery) {
- ObjPtr<mirror::String> msg = exception_ref->GetDetailMessage();
- std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : "");
- self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << exception_ref->PrettyTypeOf()
- << ": " << str_msg << "\n");
- }
-
- // Walk the stack to find catch handler.
- CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this);
- visitor.WalkStack(true);
+ MutableHandle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
+ // Sending the instrumentation events (done by the InstrumentationStackPopper) can cause new
+ // exceptions to be thrown which will override the current exception. Therefore we need to perform
+ // the search for a catch in a loop until we have successfully popped all the way to a catch or
+ // the top of the stack.
+ do {
+ if (kDebugExceptionDelivery) {
+ ObjPtr<mirror::String> msg = exception_ref->GetDetailMessage();
+ std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : "");
+ self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << exception_ref->PrettyTypeOf()
+ << ": " << str_msg << "\n");
+ }
- if (kDebugExceptionDelivery) {
- if (*handler_quick_frame_ == nullptr) {
- LOG(INFO) << "Handler is upcall";
+ // Walk the stack to find catch handler.
+ CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this, /*skip*/already_popped);
+ visitor.WalkStack(true);
+ uint32_t new_pop_count = handler_frame_depth_;
+ DCHECK_GE(new_pop_count, already_popped);
+ already_popped = new_pop_count;
+
+ // Figure out how many of those frames have instrumentation we need to remove (Should be the
+ // exact same as number of new_pop_count if there aren't inlined frames).
+ InstrumentationStackVisitor instrumentation_visitor(self_, handler_frame_depth_);
+ instrumentation_visitor.WalkStack(true);
+ size_t instrumentation_frames_to_pop = instrumentation_visitor.GetInstrumentationFramesToPop();
+
+ if (kDebugExceptionDelivery) {
+ if (*handler_quick_frame_ == nullptr) {
+ LOG(INFO) << "Handler is upcall";
+ }
+ if (handler_method_ != nullptr) {
+ const DexFile* dex_file = handler_method_->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ int line_number = annotations::GetLineNumFromPC(dex_file, handler_method_, handler_dex_pc_);
+ LOG(INFO) << "Handler: " << handler_method_->PrettyMethod() << " (line: "
+ << line_number << ")";
+ }
+ LOG(INFO) << "Will attempt to pop " << instrumentation_frames_to_pop
+ << " off of the instrumentation stack";
}
- if (handler_method_ != nullptr) {
- const DexFile* dex_file = handler_method_->GetDeclaringClass()->GetDexCache()->GetDexFile();
- int line_number = annotations::GetLineNumFromPC(dex_file, handler_method_, handler_dex_pc_);
- LOG(INFO) << "Handler: " << handler_method_->PrettyMethod() << " (line: "
- << line_number << ")";
+ // Exception was cleared as part of delivery.
+ DCHECK(!self_->IsExceptionPending());
+ // If the handler is in optimized code, we need to set the catch environment.
+ if (*handler_quick_frame_ != nullptr &&
+ handler_method_header_ != nullptr &&
+ handler_method_header_->IsOptimized()) {
+ SetCatchEnvironmentForOptimizedHandler(&visitor);
}
- }
- // Exception was cleared as part of delivery.
- DCHECK(!self_->IsExceptionPending());
+ popped_to_top = popper.PopFramesTo(instrumentation_frames_to_pop, exception_ref);
+ } while (!popped_to_top);
if (!clear_exception_) {
// Put exception back in root set with clear throw location.
self_->SetException(exception_ref.Get());
}
- // If the handler is in optimized code, we need to set the catch environment.
- if (*handler_quick_frame_ != nullptr &&
- handler_method_header_ != nullptr &&
- handler_method_header_->IsOptimized()) {
- SetCatchEnvironmentForOptimizedHandler(&visitor);
- }
}
static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) {
@@ -561,48 +638,8 @@ void QuickExceptionHandler::DeoptimizePartialFragmentFixup(uintptr_t return_pc)
}
}
-// Unwinds all instrumentation stack frame prior to catch handler or upcall.
-class InstrumentationStackVisitor : public StackVisitor {
- public:
- InstrumentationStackVisitor(Thread* self, size_t frame_depth)
- REQUIRES_SHARED(Locks::mutator_lock_)
- : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- frame_depth_(frame_depth),
- instrumentation_frames_to_pop_(0) {
- CHECK_NE(frame_depth_, kInvalidFrameDepth);
- }
-
- bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
- size_t current_frame_depth = GetFrameDepth();
- if (current_frame_depth < frame_depth_) {
- CHECK(GetMethod() != nullptr);
- if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) {
- if (!IsInInlinedFrame()) {
- // We do not count inlined frames, because we do not instrument them. The reason we
- // include them in the stack walking is the check against `frame_depth_`, which is
- // given to us by a visitor that visits inlined frames.
- ++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:
- const size_t frame_depth_;
- size_t instrumentation_frames_to_pop_;
-
- DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor);
-};
-
uintptr_t QuickExceptionHandler::UpdateInstrumentationStack() {
+ DCHECK(is_deoptimization_) << "Non-deoptimization handlers should use FindCatch";
uintptr_t return_pc = 0;
if (method_tracing_active_) {
InstrumentationStackVisitor visitor(self_, handler_frame_depth_);
@@ -610,9 +647,7 @@ uintptr_t QuickExceptionHandler::UpdateInstrumentationStack() {
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) {
- return_pc = instrumentation->PopMethodForUnwind(self_, is_deoptimization_);
- }
+ return_pc = instrumentation->PopFramesForDeoptimization(self_, instrumentation_frames_to_pop);
}
return return_pc;
}