Make QuickExceptionHandler handle force-retry-instruction

If the runtime is using instrumentation trampolines (i.e. performing
DDMS tracing) then we need to wait until the actual
QuickExceptionHandler to perform the deopt to keep the instrumentation
stack sane. Previously we were only checking force-pop-frame at this
point since normally force-retry-instruction would be handled earlier.
This fixes this oversight.

Test: ./test/testrunner/testrunner.py --host --trace --ntrace
Bug: 73255278
Bug: 111357976
Bug: 117533193
Bug: 117615146
Change-Id: I372b8d9c809ba6db085fb074c96d83889d0b0c2c
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2fb63a1..31a6036 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3372,8 +3372,13 @@
   // 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.
+  //
+  // Note we need to check for both force_frame_pop and force_retry_instruction. The first is
+  // expected to happen fairly regularly but the second can only happen if we are using
+  // instrumentation trampolines (for example with DDMS tracing). That forces us to do deopt later
+  // and see every frame being popped. We don't need to handle it any differently.
   ShadowFrame* cf;
-  bool force_frame_pop = false;
+  bool force_deopt;
   {
     NthCallerVisitor visitor(this, 0, false);
     visitor.WalkStack();
@@ -3381,7 +3386,8 @@
     if (cf == nullptr) {
       cf = FindDebuggerShadowFrame(visitor.GetFrameId());
     }
-    force_frame_pop = cf != nullptr && cf->GetForcePopFrame();
+    bool force_frame_pop = cf != nullptr && cf->GetForcePopFrame();
+    bool force_retry_instr = cf != nullptr && cf->GetForceRetryInstruction();
     if (kIsDebugBuild && force_frame_pop) {
       NthCallerVisitor penultimate_visitor(this, 1, false);
       penultimate_visitor.WalkStack();
@@ -3394,8 +3400,9 @@
           << "Force pop frame without retry instruction found. penultimate frame is null: "
           << (penultimate_frame == nullptr ? "true" : "false");
     }
+    force_deopt = force_frame_pop || force_retry_instr;
   }
-  if (Dbg::IsForcedInterpreterNeededForException(this) || force_frame_pop) {
+  if (Dbg::IsForcedInterpreterNeededForException(this) || force_deopt) {
     NthCallerVisitor visitor(this, 0, false);
     visitor.WalkStack();
     if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
@@ -3403,16 +3410,18 @@
       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) {
+      if (force_deopt) {
         VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop";
         DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
         // Get rid of the exception since we are doing a framepop instead.
+        LOG(WARNING) << "Suppressing pending exception for retry-instruction/frame-pop: "
+                     << exception->Dump();
         ClearException();
       }
       PushDeoptimizationContext(
           JValue(),
           false /* is_reference */,
-          (force_frame_pop ? nullptr : exception),
+          (force_deopt ? nullptr : exception),
           false /* from_code */,
           method_type);
       artDeoptimize(this);