JVMTI PopFrame support

Implement support for the JVMTI can_pop_frames capability. This works
by marking shadow-frames with a bit that forces it to be popped or an
instruction to be retried. When a PopFrame is requested the plugin
will deoptimize the targeted thread and force the interpreter to deal
with the frame pop. If the can_pop_frames capability is enabled the
runtime will be forced to handle all exceptions through the
interpreter. This is required to support PopFrame during some
exception events.

Test: ./test.py --host
Test: ./art/tools/run-libjdwp-tests.sh --mode=host
Bug: 73255278
Bug: 111357976
Change-Id: I62d6b1f4ff387c794ba45093c3d6773aaf642067
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4a3d8cb..afb2c28 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3368,22 +3368,51 @@
     HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
     instrumentation->ExceptionThrownEvent(this, exception.Ptr());
   }
-  // Does instrumentation need to deoptimize the stack?
-  // Note: we do this *after* reporting the exception to instrumentation in case it
-  // now requires deoptimization. It may happen if a debugger is attached and requests
-  // new events (single-step, breakpoint, ...) when the exception is reported.
-  if (Dbg::IsForcedInterpreterNeededForException(this)) {
+  // Does instrumentation need to deoptimize the stack or otherwise go to interpreter for something?
+  // Note: we do this *after* reporting the exception to instrumentation in case it now requires
+  // deoptimization. It may happen if a debugger is attached and requests new events (single-step,
+  // breakpoint, ...) when the exception is reported.
+  ShadowFrame* cf;
+  bool force_frame_pop = false;
+  {
+    NthCallerVisitor visitor(this, 0, false);
+    visitor.WalkStack();
+    cf = visitor.GetCurrentShadowFrame();
+    if (cf == nullptr) {
+      cf = FindDebuggerShadowFrame(visitor.GetFrameId());
+    }
+    force_frame_pop = cf != nullptr && cf->GetForcePopFrame();
+    if (kIsDebugBuild && force_frame_pop) {
+      NthCallerVisitor penultimate_visitor(this, 1, false);
+      penultimate_visitor.WalkStack();
+      ShadowFrame* penultimate_frame = penultimate_visitor.GetCurrentShadowFrame();
+      if (penultimate_frame == nullptr) {
+        penultimate_frame = FindDebuggerShadowFrame(penultimate_visitor.GetFrameId());
+      }
+      DCHECK(penultimate_frame != nullptr &&
+             penultimate_frame->GetForceRetryInstruction())
+          << "Force pop frame without retry instruction found. penultimate frame is null: "
+          << (penultimate_frame == nullptr ? "true" : "false");
+    }
+  }
+  if (Dbg::IsForcedInterpreterNeededForException(this) || force_frame_pop) {
     NthCallerVisitor visitor(this, 0, false);
     visitor.WalkStack();
     if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
+      VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop";
       // method_type shouldn't matter due to exception handling.
       const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
       // Save the exception into the deoptimization context so it can be restored
       // before entering the interpreter.
+      if (force_frame_pop) {
+        DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+        // Get rid of the exception since we are doing a framepop instead.
+        ClearException();
+      }
       PushDeoptimizationContext(
           JValue(),
           false /* is_reference */,
-          exception,
+          (force_frame_pop ? nullptr : exception),
           false /* from_code */,
           method_type);
       artDeoptimize(this);