Merge "Fix exception reporting from interpreter"
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 73ed590..a0cecb0 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3430,6 +3430,7 @@
   auto old_throw_method = hs.NewHandle<mirror::ArtMethod>(nullptr);
   auto old_exception = hs.NewHandle<mirror::Throwable>(nullptr);
   uint32_t old_throw_dex_pc;
+  bool old_exception_report_flag;
   {
     ThrowLocation old_throw_location;
     mirror::Throwable* old_exception_obj = soa.Self()->GetException(&old_throw_location);
@@ -3437,6 +3438,7 @@
     old_throw_method.Assign(old_throw_location.GetMethod());
     old_exception.Assign(old_exception_obj);
     old_throw_dex_pc = old_throw_location.GetDexPc();
+    old_exception_report_flag = soa.Self()->IsExceptionReportedToInstrumentation();
     soa.Self()->ClearException();
   }
 
@@ -3491,6 +3493,7 @@
     ThrowLocation gc_safe_throw_location(old_throw_this_object.Get(), old_throw_method.Get(),
                                          old_throw_dex_pc);
     soa.Self()->SetException(gc_safe_throw_location, old_exception.Get());
+    soa.Self()->SetExceptionReportedToInstrumentation(old_exception_report_flag);
   }
 }
 
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 09899c0..3d8b29f 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -652,6 +652,7 @@
   // Save any pending exception over monitor exit call.
   mirror::Throwable* saved_exception = NULL;
   ThrowLocation saved_throw_location;
+  bool is_exception_reported = self->IsExceptionReportedToInstrumentation();
   if (UNLIKELY(self->IsExceptionPending())) {
     saved_exception = self->GetException(&saved_throw_location);
     self->ClearException();
@@ -667,6 +668,7 @@
   // Restore pending exception.
   if (saved_exception != NULL) {
     self->SetException(saved_throw_location, saved_exception);
+    self->SetExceptionReportedToInstrumentation(is_exception_reported);
   }
 }
 
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 261c241..8f5da83 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -780,24 +780,20 @@
 void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
                                          mirror::ArtMethod* method, uint32_t dex_pc,
                                          mirror::ArtField* field) const {
-  if (have_field_read_listeners_) {
-    // TODO: same comment than DexPcMovedEventImpl.
-    std::list<InstrumentationListener*> copy(field_read_listeners_);
-    for (InstrumentationListener* listener : copy) {
-      listener->FieldRead(thread, this_object, method, dex_pc, field);
-    }
+  // TODO: same comment than DexPcMovedEventImpl.
+  std::list<InstrumentationListener*> copy(field_read_listeners_);
+  for (InstrumentationListener* listener : copy) {
+    listener->FieldRead(thread, this_object, method, dex_pc, field);
   }
 }
 
 void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
                                          mirror::ArtMethod* method, uint32_t dex_pc,
                                          mirror::ArtField* field, const JValue& field_value) const {
-  if (have_field_write_listeners_) {
-    // TODO: same comment than DexPcMovedEventImpl.
-    std::list<InstrumentationListener*> copy(field_write_listeners_);
-    for (InstrumentationListener* listener : copy) {
-      listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
-    }
+  // TODO: same comment than DexPcMovedEventImpl.
+  std::list<InstrumentationListener*> copy(field_write_listeners_);
+  for (InstrumentationListener* listener : copy) {
+    listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
   }
 }
 
@@ -805,8 +801,9 @@
                                            mirror::ArtMethod* catch_method,
                                            uint32_t catch_dex_pc,
                                            mirror::Throwable* exception_object) const {
-  if (have_exception_caught_listeners_) {
-    DCHECK_EQ(thread->GetException(NULL), exception_object);
+  if (HasExceptionCaughtListeners()) {
+    DCHECK_EQ(thread->GetException(nullptr), exception_object);
+    bool is_exception_reported = thread->IsExceptionReportedToInstrumentation();
     thread->ClearException();
     // TODO: The copy below is due to the debug listener having an action where it can remove
     // itself as a listener and break the iterator. The copy only works around the problem.
@@ -815,6 +812,7 @@
       listener->ExceptionCaught(thread, throw_location, catch_method, catch_dex_pc, exception_object);
     }
     thread->SetException(throw_location, exception_object);
+    thread->SetExceptionReportedToInstrumentation(is_exception_reported);
   }
 }
 
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 6625801..d0cb4de 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -237,6 +237,10 @@
     return have_field_write_listeners_;
   }
 
+  bool HasExceptionCaughtListeners() const {
+    return have_exception_caught_listeners_;
+  }
+
   bool IsActive() const {
     return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
         have_field_read_listeners_ || have_field_write_listeners_ ||
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index e1fe563..c7fb884 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -372,31 +372,107 @@
 #undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
 
+/**
+ * Finds the location where this exception will be caught. We search until we reach either the top
+ * frame or a native frame, in which cases this exception is considered uncaught.
+ */
+class CatchLocationFinder : public StackVisitor {
+ public:
+  explicit CatchLocationFinder(Thread* self, Handle<mirror::Throwable>* exception)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+    : StackVisitor(self, nullptr), self_(self), handle_scope_(self), exception_(exception),
+      catch_method_(handle_scope_.NewHandle<mirror::ArtMethod>(nullptr)),
+      catch_dex_pc_(DexFile::kDexNoIndex), clear_exception_(false) {
+  }
+
+  bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* method = GetMethod();
+    if (method == nullptr) {
+      return true;
+    }
+    if (method->IsRuntimeMethod()) {
+      // Ignore callee save method.
+      DCHECK(method->IsCalleeSaveMethod());
+      return true;
+    }
+    if (method->IsNative()) {
+      return false;  // End stack walk.
+    }
+    DCHECK(!method->IsNative());
+    uint32_t dex_pc = GetDexPc();
+    if (dex_pc != DexFile::kDexNoIndex) {
+      uint32_t found_dex_pc;
+      {
+        StackHandleScope<3> hs(self_);
+        Handle<mirror::Class> exception_class(hs.NewHandle((*exception_)->GetClass()));
+        Handle<mirror::ArtMethod> h_method(hs.NewHandle(method));
+        found_dex_pc = mirror::ArtMethod::FindCatchBlock(h_method, exception_class, dex_pc,
+                                                         &clear_exception_);
+      }
+      if (found_dex_pc != DexFile::kDexNoIndex) {
+        catch_method_.Assign(method);
+        catch_dex_pc_ = found_dex_pc;
+        return false;  // End stack walk.
+      }
+    }
+    return true;  // Continue stack walk.
+  }
+
+  ArtMethod* GetCatchMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return catch_method_.Get();
+  }
+
+  uint32_t GetCatchDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return catch_dex_pc_;
+  }
+
+  bool NeedClearException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return clear_exception_;
+  }
+
+ private:
+  Thread* const self_;
+  StackHandleScope<1> handle_scope_;
+  Handle<mirror::Throwable>* exception_;
+  Handle<mirror::ArtMethod> catch_method_;
+  uint32_t catch_dex_pc_;
+  bool clear_exception_;
+
+
+  DISALLOW_COPY_AND_ASSIGN(CatchLocationFinder);
+};
+
 uint32_t FindNextInstructionFollowingException(Thread* self,
                                                ShadowFrame& shadow_frame,
                                                uint32_t dex_pc,
-                                               mirror::Object* this_object,
                                                const instrumentation::Instrumentation* instrumentation) {
   self->VerifyStack();
   ThrowLocation throw_location;
-  mirror::Throwable* exception = self->GetException(&throw_location);
+  StackHandleScope<3> hs(self);
+  Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException(&throw_location)));
+  if (!self->IsExceptionReportedToInstrumentation() && instrumentation->HasExceptionCaughtListeners()) {
+    CatchLocationFinder clf(self, &exception);
+    clf.WalkStack(false);
+    instrumentation->ExceptionCaughtEvent(self, throw_location, clf.GetCatchMethod(),
+                                          clf.GetCatchDexPc(), exception.Get());
+    self->SetExceptionReportedToInstrumentation(true);
+  }
   bool clear_exception = false;
   uint32_t found_dex_pc;
   {
-    StackHandleScope<3> hs(self);
     Handle<mirror::Class> exception_class(hs.NewHandle(exception->GetClass()));
     Handle<mirror::ArtMethod> h_method(hs.NewHandle(shadow_frame.GetMethod()));
-    HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(&this_object));
     found_dex_pc = mirror::ArtMethod::FindCatchBlock(h_method, exception_class, dex_pc,
                                                      &clear_exception);
   }
   if (found_dex_pc == DexFile::kDexNoIndex) {
-    instrumentation->MethodUnwindEvent(self, this_object,
+    instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
                                        shadow_frame.GetMethod(), dex_pc);
   } else {
-    instrumentation->ExceptionCaughtEvent(self, throw_location,
-                                          shadow_frame.GetMethod(),
-                                          found_dex_pc, exception);
+    if (self->IsExceptionReportedToInstrumentation()) {
+      instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
+                                         shadow_frame.GetMethod(), dex_pc);
+    }
     if (clear_exception) {
       self->ClearException();
     }
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 0c69fe9..d18f9f9 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -334,8 +334,7 @@
 }
 
 uint32_t FindNextInstructionFollowingException(Thread* self, ShadowFrame& shadow_frame,
-    uint32_t dex_pc, mirror::Object* this_object,
-    const instrumentation::Instrumentation* instrumentation)
+    uint32_t dex_pc, const instrumentation::Instrumentation* instrumentation)
         SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 19673ac..cb4868c 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -2391,10 +2391,8 @@
       CheckSuspend(self);
       UPDATE_HANDLER_TABLE();
     }
-    Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc,
-                                                                  this_object,
                                                                   instrumentation);
     if (found_dex_pc == DexFile::kDexNoIndex) {
       return JValue(); /* Handled in caller. */
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index a43fad3..bdf2a20 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -25,10 +25,8 @@
     if (UNLIKELY(self->TestAllFlags())) {                                                       \
       CheckSuspend(self);                                                                       \
     }                                                                                           \
-    Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);                     \
     uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame,           \
                                                                   inst->GetDexPc(insns),        \
-                                                                  this_object,                  \
                                                                   instrumentation);             \
     if (found_dex_pc == DexFile::kDexNoIndex) {                                                 \
       return JValue(); /* Handled in caller. */                                                 \
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 19ee1ff..66406bf 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -682,6 +682,7 @@
     auto old_throw_method(hs.NewHandle<mirror::ArtMethod>(nullptr));
     auto old_exception(hs.NewHandle<mirror::Throwable>(nullptr));
     uint32_t old_throw_dex_pc;
+    bool old_is_exception_reported;
     {
       ThrowLocation old_throw_location;
       mirror::Throwable* old_exception_obj = soa.Self()->GetException(&old_throw_location);
@@ -689,6 +690,7 @@
       old_throw_method.Assign(old_throw_location.GetMethod());
       old_exception.Assign(old_exception_obj);
       old_throw_dex_pc = old_throw_location.GetDexPc();
+      old_is_exception_reported = soa.Self()->IsExceptionReportedToInstrumentation();
       soa.Self()->ClearException();
     }
     ScopedLocalRef<jthrowable> exception(env,
@@ -710,6 +712,7 @@
                                          old_throw_dex_pc);
 
     soa.Self()->SetException(gc_safe_throw_location, old_exception.Get());
+    soa.Self()->SetExceptionReportedToInstrumentation(old_is_exception_reported);
   }
 
   static jthrowable ExceptionOccurred(JNIEnv* env) {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 3db4be3..4821e29 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -240,6 +240,7 @@
   ThrowLocation throw_location;
   StackHandleScope<1> hs(self);
   Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException(&throw_location)));
+  bool is_exception_reported = self->IsExceptionReportedToInstrumentation();
   self->ClearException();
   // Default to handler not found.
   uint32_t found_dex_pc = DexFile::kDexNoIndex;
@@ -276,6 +277,7 @@
   // Put the exception back.
   if (exception.Get() != nullptr) {
     self->SetException(throw_location, exception.Get());
+    self->SetExceptionReportedToInstrumentation(is_exception_reported);
   }
   return found_dex_pc;
 }
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 7b31a82..a20f7b9 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -84,7 +84,7 @@
     Handle<mirror::Object> old_throw_this_object(hs.NewHandle(old_throw_location.GetThis()));
     Handle<mirror::ArtMethod> old_throw_method(hs.NewHandle(old_throw_location.GetMethod()));
     uint32_t old_throw_dex_pc = old_throw_location.GetDexPc();
-
+    bool is_exception_reported = self->IsExceptionReportedToInstrumentation();
     // clear exception to call FindSystemClass
     self->ClearException();
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -102,8 +102,8 @@
     // Restore exception.
     ThrowLocation gc_safe_throw_location(old_throw_this_object.Get(), old_throw_method.Get(),
                                          old_throw_dex_pc);
-
     self->SetException(gc_safe_throw_location, old_exception.Get());
+    self->SetExceptionReportedToInstrumentation(is_exception_reported);
   }
   COMPILE_ASSERT(sizeof(Status) == sizeof(uint32_t), size_of_status_not_uint32);
   if (Runtime::Current()->IsActiveTransaction()) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ecd1983..f4721f2 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '4', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '3', '5', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index e3f9afc..1034923 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -110,7 +110,8 @@
 };
 
 void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location,
-                                      mirror::Throwable* exception) {
+                                      mirror::Throwable* exception,
+                                      bool is_exception_reported) {
   DCHECK(!is_deoptimization_);
   if (kDebugExceptionDelivery) {
     mirror::String* msg = exception->GetDetailMessage();
@@ -141,12 +142,24 @@
   } else {
     // Put exception back in root set with clear throw location.
     self_->SetException(ThrowLocation(), exception_ref.Get());
+    self_->SetExceptionReportedToInstrumentation(is_exception_reported);
   }
   // 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, handler_method_, handler_dex_pc_,
-                                        exception_ref.Get());
+  if (!is_exception_reported) {
+    instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+    instrumentation->ExceptionCaughtEvent(self_, throw_location, handler_method_, handler_dex_pc_,
+                                          exception_ref.Get());
+      // We're not catching this exception but let's remind we already reported the exception above
+      // to avoid reporting it twice.
+      self_->SetExceptionReportedToInstrumentation(true);
+  }
+  bool caught_exception = (handler_method_ != nullptr && handler_dex_pc_ != DexFile::kDexNoIndex);
+  if (caught_exception) {
+    // We're catching this exception so we finish reporting it. We do it here to avoid doing it
+    // in the compiled code.
+    self_->SetExceptionReportedToInstrumentation(false);
+  }
 }
 
 // Prepares deoptimization.
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index a4229b3..1d600ed 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -42,7 +42,8 @@
     LOG(FATAL) << "UNREACHABLE";  // Expected to take long jump.
   }
 
-  void FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception)
+  void FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception,
+                 bool is_exception_reported)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void DeoptimizeStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void UpdateInstrumentationStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 7f7b542..021c7c1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1611,6 +1611,7 @@
   Handle<mirror::ArtMethod> saved_throw_method(hs.NewHandle(throw_location.GetMethod()));
   // Ignore the cause throw location. TODO: should we report this as a re-throw?
   ScopedLocalRef<jobject> cause(GetJniEnv(), soa.AddLocalReference<jobject>(GetException(nullptr)));
+  bool is_exception_reported = IsExceptionReportedToInstrumentation();
   ClearException();
   Runtime* runtime = Runtime::Current();
 
@@ -1641,6 +1642,7 @@
     ThrowLocation gc_safe_throw_location(saved_throw_this.Get(), saved_throw_method.Get(),
                                          throw_location.GetDexPc());
     SetException(gc_safe_throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError());
+    SetExceptionReportedToInstrumentation(is_exception_reported);
     return;
   }
 
@@ -1693,6 +1695,7 @@
     ThrowLocation gc_safe_throw_location(saved_throw_this.Get(), saved_throw_method.Get(),
                                          throw_location.GetDexPc());
     SetException(gc_safe_throw_location, exception.Get());
+    SetExceptionReportedToInstrumentation(is_exception_reported);
   } else {
     jvalue jv_args[2];
     size_t i = 0;
@@ -1710,6 +1713,7 @@
       ThrowLocation gc_safe_throw_location(saved_throw_this.Get(), saved_throw_method.Get(),
                                            throw_location.GetDexPc());
       SetException(gc_safe_throw_location, exception.Get());
+      SetExceptionReportedToInstrumentation(is_exception_reported);
     }
   }
 }
@@ -1892,13 +1896,14 @@
   CHECK(exception != nullptr);
   // Don't leave exception visible while we try to find the handler, which may cause class
   // resolution.
+  bool is_exception_reported = IsExceptionReportedToInstrumentation();
   ClearException();
   bool is_deoptimization = (exception == GetDeoptimizationException());
   QuickExceptionHandler exception_handler(this, is_deoptimization);
   if (is_deoptimization) {
     exception_handler.DeoptimizeStack();
   } else {
-    exception_handler.FindCatch(throw_location, exception);
+    exception_handler.FindCatch(throw_location, exception, is_exception_reported);
   }
   exception_handler.UpdateInstrumentationStack();
   exception_handler.DoLongJump();
diff --git a/runtime/thread.h b/runtime/thread.h
index 5de54b3..bff9b52 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -329,6 +329,7 @@
   void ClearException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     tlsPtr_.exception = nullptr;
     tlsPtr_.throw_location.Clear();
+    SetExceptionReportedToInstrumentation(false);
   }
 
   // Find catch block and perform long jump to appropriate exception handle
@@ -809,6 +810,14 @@
     tlsPtr_.rosalloc_runs[index] = run;
   }
 
+  bool IsExceptionReportedToInstrumentation() const {
+    return tls32_.is_exception_reported_to_instrumentation_;
+  }
+
+  void SetExceptionReportedToInstrumentation(bool reported) {
+    tls32_.is_exception_reported_to_instrumentation_ = reported;
+  }
+
  private:
   explicit Thread(bool daemon);
   ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
@@ -911,7 +920,7 @@
     explicit tls_32bit_sized_values(bool is_daemon) :
       suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
       daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
-      thread_exit_check_count(0) {
+      thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false) {
     }
 
     union StateAndFlags state_and_flags;
@@ -947,6 +956,10 @@
 
     // How many times has our pthread key's destructor been called?
     uint32_t thread_exit_check_count;
+
+    // When true this field indicates that the exception associated with this thread has already
+    // been reported to instrumentation.
+    bool32_t is_exception_reported_to_instrumentation_;
   } tls32_;
 
   struct PACKED(8) tls_64bit_sized_values {