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
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
new file mode 100644
index 0000000..d5844b6
--- /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<mirror::Throwable> 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<uintptr_t>(handler_quick_frame_));
+  CHECK_NE(handler_quick_frame_pc_, 0u);
+  context_->SetPC(handler_quick_frame_pc_);
+  context_->SmashCallerSaves();
+  context_->DoLongJump();
+}
+
+}  // namespace art