diff options
| author | 2024-05-21 09:08:32 +0000 | |
|---|---|---|
| committer | 2024-05-22 13:07:23 +0000 | |
| commit | a11d8246f846148c83bbcbbe39511c460cc0319a (patch) | |
| tree | c68e33f67340e7872086993c1124cfc87d875be3 | |
| parent | d3f3b2b2790f773c6281730802f52f894d800384 (diff) | |
Refactor instrumentation handling in interpreter.
The transactional interpreter does not need to check for
instrumentation events.
Also change `PerformNonStandartReturn()` which is used only
for unusual code paths (only for instrumentation and aborted
transactions) from `ALWAYS_INLINE` to non-inline and remove
some dead code from it.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --interp-ac
Change-Id: I2b00c82eac480dd3291f2c7cd68430e606804eaf
| -rw-r--r-- | runtime/interpreter/interpreter.cc | 2 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.cc | 20 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.h | 28 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_switch_impl-inl.h | 113 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_switch_impl0.cc | 99 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_switch_impl1.cc | 80 |
6 files changed, 249 insertions, 93 deletions
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 6097077fe2..fb6f36d5e7 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -309,7 +309,6 @@ static inline JValue Execute( shadow_frame, ret, instrumentation, - accessor.InsSize(), /* unlock_monitors= */ false); return ret; } @@ -324,7 +323,6 @@ static inline JValue Execute( shadow_frame, ret, instrumentation, - accessor.InsSize(), /* unlock_monitors= */ false); } return ret; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 280136d6a2..4683cd50ee 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1542,6 +1542,26 @@ void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame) } } +void PerformNonStandardReturn(Thread* self, + ShadowFrame& frame, + JValue& result, + const instrumentation::Instrumentation* instrumentation, + bool unlock_monitors) { + if (UNLIKELY(self->IsExceptionPending())) { + LOG(WARNING) << "Suppressing exception for non-standard method exit: " + << self->GetException()->Dump(); + self->ClearException(); + } + if (unlock_monitors) { + UnlockHeldMonitors(self, &frame); + DoMonitorCheckOnExit(self, &frame); + } + result = JValue(); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) { + SendMethodExitEvents(self, instrumentation, frame, frame.GetMethod(), result); + } +} + // Explicit DoCall template function declarations. #define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range) \ template REQUIRES_SHARED(Locks::mutator_lock_) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index ba61562d9b..2001e89b9d 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -149,29 +149,11 @@ NeedsMethodExitEvent(const instrumentation::Instrumentation* ins) COLD_ATTR void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_); -static inline ALWAYS_INLINE void PerformNonStandardReturn( - Thread* self, - ShadowFrame& frame, - JValue& result, - const instrumentation::Instrumentation* instrumentation, - uint16_t num_dex_inst, - bool unlock_monitors = true) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Object> thiz(frame.GetThisObject(num_dex_inst)); - StackHandleScope<1u> hs(self); - if (UNLIKELY(self->IsExceptionPending())) { - LOG(WARNING) << "Suppressing exception for non-standard method exit: " - << self->GetException()->Dump(); - self->ClearException(); - } - if (unlock_monitors) { - UnlockHeldMonitors(self, &frame); - DoMonitorCheckOnExit(self, &frame); - } - result = JValue(); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) { - SendMethodExitEvents(self, instrumentation, frame, frame.GetMethod(), result); - } -} +void PerformNonStandardReturn(Thread* self, + ShadowFrame& frame, + JValue& result, + const instrumentation::Instrumentation* instrumentation, + bool unlock_monitors = true) REQUIRES_SHARED(Locks::mutator_lock_); // Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range]. // Returns true on success, otherwise throws an exception and returns false. diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h index 35708b00c6..222f3775bc 100644 --- a/runtime/interpreter/interpreter_switch_impl-inl.h +++ b/runtime/interpreter/interpreter_switch_impl-inl.h @@ -49,6 +49,11 @@ namespace interpreter { class ActiveTransactionChecker; // For transactional interpreter. class InactiveTransactionChecker; // For non-transactional interpreter. +// We declare the helpers classes for instrumentation handling here but they shall be defined +// only when compiling the transactional and non-transactional interpreter. +class ActiveInstrumentationHandler; // For non-transactional interpreter. +class InactiveInstrumentationHandler; // For transactional interpreter. + // Handles iget-XXX and sget-XXX instructions. // Returns true on success, otherwise throws an exception and returns false. template<FindFieldType find_type, @@ -57,9 +62,13 @@ template<FindFieldType find_type, ALWAYS_INLINE bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, - uint16_t inst_data) REQUIRES_SHARED(Locks::mutator_lock_) { + uint16_t inst_data, + const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + using InstrumentationHandler = typename std::conditional_t< + transaction_active, InactiveInstrumentationHandler, ActiveInstrumentationHandler>; + bool should_report = InstrumentationHandler::HasFieldReadListeners(instrumentation); const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); - bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldReadListeners(); ArtField* field = nullptr; MemberOffset offset(0u); bool is_volatile; @@ -150,9 +159,12 @@ template<FindFieldType find_type, Primitive::Type field_type, bool transaction_a ALWAYS_INLINE bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, - uint16_t inst_data) + uint16_t inst_data, + const instrumentation::Instrumentation* instrumentation) REQUIRES_SHARED(Locks::mutator_lock_) { - bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldWriteListeners(); + using InstrumentationHandler = typename std::conditional_t< + transaction_active, InactiveInstrumentationHandler, ActiveInstrumentationHandler>; + bool should_report = InstrumentationHandler::HasFieldWriteListeners(instrumentation); bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); bool resolve_field_type = (shadow_frame.GetVRegReference(vregA) != nullptr); @@ -254,6 +266,8 @@ ALWAYS_INLINE bool DoFieldPut(Thread* self, template<bool transaction_active, Instruction::Format kFormat> class InstructionHandler { public: + using InstrumentationHandler = typename std::conditional_t< + transaction_active, InactiveInstrumentationHandler, ActiveInstrumentationHandler>; using TransactionChecker = typename std::conditional_t< transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>; @@ -268,8 +282,7 @@ class InstructionHandler { DCHECK(abort_exception != nullptr); DCHECK(abort_exception->GetClass()->DescriptorEquals(kTransactionAbortErrorDescriptor)); Self()->ClearException(); - PerformNonStandardReturn( - Self(), shadow_frame_, ctx_->result, Instrumentation(), Accessor().InsSize()); + PerformNonStandardReturn(Self(), shadow_frame_, ctx_->result, Instrumentation()); Self()->SetException(abort_exception.Get()); ExitInterpreterLoop(); return false; @@ -278,10 +291,9 @@ class InstructionHandler { } HANDLER_ATTRIBUTES bool CheckForceReturn() { - if (shadow_frame_.GetForcePopFrame()) { + if (InstrumentationHandler::GetForcePopFrame(shadow_frame_)) { DCHECK(Runtime::Current()->AreNonStandardExitsEnabled()); - PerformNonStandardReturn( - Self(), shadow_frame_, ctx_->result, Instrumentation(), Accessor().InsSize()); + PerformNonStandardReturn(Self(), shadow_frame_, ctx_->result, Instrumentation()); ExitInterpreterLoop(); return false; } @@ -348,16 +360,16 @@ class InstructionHandler { if (!CheckForceReturn()) { return false; } - if (UNLIKELY(shadow_frame_.GetNotifyDexPcMoveEvents())) { + if (UNLIKELY(InstrumentationHandler::NeedsDexPcEvents(shadow_frame_))) { uint8_t opcode = inst_->Opcode(inst_data_); bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT); JValue* save_ref = is_move_result_object ? &ctx_->result_register : nullptr; - if (UNLIKELY(!DoDexPcMoveEvent(Self(), - Accessor(), - shadow_frame_, - DexPC(), - Instrumentation(), - save_ref))) { + if (UNLIKELY(!InstrumentationHandler::DoDexPcMoveEvent(Self(), + Accessor(), + shadow_frame_, + DexPC(), + Instrumentation(), + save_ref))) { DCHECK(Self()->IsExceptionPending()); // Do not raise exception event if it is caused by other instrumentation event. shadow_frame_.SetSkipNextExceptionEvent(true); @@ -370,53 +382,17 @@ class InstructionHandler { return true; } - // Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if - // the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able - // to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by - // jvmti-agents while handling breakpoint or single step events. We had to move this into its own - // function because it was making ExecuteSwitchImpl have too large a stack. - NO_INLINE static bool DoDexPcMoveEvent(Thread* self, - const CodeItemDataAccessor& accessor, - const ShadowFrame& shadow_frame, - uint32_t dex_pc_, - const instrumentation::Instrumentation* instrumentation, - JValue* save_ref) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(instrumentation->HasDexPcListeners()); - StackHandleScope<2> hs(self); - Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException())); - mirror::Object* null_obj = nullptr; - HandleWrapper<mirror::Object> h( - hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); - self->ClearException(); - instrumentation->DexPcMovedEvent(self, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - dex_pc_); - if (UNLIKELY(self->IsExceptionPending())) { - // We got a new exception in the dex-pc-moved event. - // We just let this exception replace the old one. - // TODO It would be good to add the old exception to the - // suppressed exceptions of the new one if possible. - return false; // Pending exception. - } - if (UNLIKELY(!thr.IsNull())) { - self->SetException(thr.Get()); - } - return true; - } - HANDLER_ATTRIBUTES bool HandleReturn(JValue result) { Self()->AllowThreadSuspension(); if (!DoMonitorCheckOnExit(Self(), &shadow_frame_)) { return false; } - if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()) && - !SendMethodExitEvents(Self(), - Instrumentation(), - shadow_frame_, - shadow_frame_.GetMethod(), - result))) { + if (UNLIKELY(InstrumentationHandler::NeedsMethodExitEvent(Instrumentation()) && + !InstrumentationHandler::SendMethodExitEvents(Self(), + Instrumentation(), + shadow_frame_, + shadow_frame_.GetMethod(), + result))) { DCHECK(Self()->IsExceptionPending()); // Do not raise exception event if it is caused by other instrumentation event. shadow_frame_.SetSkipNextExceptionEvent(true); @@ -431,8 +407,9 @@ class InstructionHandler { if (UNLIKELY(Self()->ObserveAsyncException())) { return false; // Pending exception. } - if (UNLIKELY(Instrumentation()->HasBranchListeners())) { - Instrumentation()->Branch(Self(), shadow_frame_.GetMethod(), DexPC(), offset); + if (UNLIKELY(InstrumentationHandler::HasBranchListeners(Instrumentation()))) { + InstrumentationHandler::Branch( + Self(), shadow_frame_.GetMethod(), DexPC(), offset, Instrumentation()); } if (!transaction_active) { // TODO: Do OSR only on back-edges and check if OSR code is ready here. @@ -546,13 +523,13 @@ class InstructionHandler { template<FindFieldType find_type, Primitive::Type field_type> HANDLER_ATTRIBUTES bool HandleGet() { return DoFieldGet<find_type, field_type, transaction_active>( - Self(), shadow_frame_, inst_, inst_data_); + Self(), shadow_frame_, inst_, inst_data_, Instrumentation()); } template<FindFieldType find_type, Primitive::Type field_type> HANDLER_ATTRIBUTES bool HandlePut() { return DoFieldPut<find_type, field_type, transaction_active>( - Self(), shadow_frame_, inst_, inst_data_); + Self(), shadow_frame_, inst_, inst_data_, Instrumentation()); } template<InvokeType type, bool is_range> @@ -686,14 +663,14 @@ class InstructionHandler { } } result.SetL(obj_result); - if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()))) { + if (UNLIKELY(InstrumentationHandler::NeedsMethodExitEvent(Instrumentation()))) { StackHandleScope<1> hs(Self()); MutableHandle<mirror::Object> h_result(hs.NewHandle(obj_result)); - if (!SendMethodExitEvents(Self(), - Instrumentation(), - shadow_frame_, - shadow_frame_.GetMethod(), - h_result)) { + if (!InstrumentationHandler::SendMethodExitEvents(Self(), + Instrumentation(), + shadow_frame_, + shadow_frame_.GetMethod(), + h_result)) { DCHECK(Self()->IsExceptionPending()); // Do not raise exception event if it is caused by other instrumentation event. shadow_frame_.SetSkipNextExceptionEvent(true); diff --git a/runtime/interpreter/interpreter_switch_impl0.cc b/runtime/interpreter/interpreter_switch_impl0.cc index 517cce8088..378b6895a5 100644 --- a/runtime/interpreter/interpreter_switch_impl0.cc +++ b/runtime/interpreter/interpreter_switch_impl0.cc @@ -58,6 +58,105 @@ class InactiveTransactionChecker { REQUIRES_SHARED(Locks::mutator_lock_) {} }; +class ActiveInstrumentationHandler { + public: + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldReadListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return instrumentation->HasFieldReadListeners(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldWriteListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return instrumentation->HasFieldWriteListeners(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasBranchListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return instrumentation->HasBranchListeners(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsDexPcEvents(ShadowFrame& shadow_frame) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_IMPLIES(shadow_frame.GetNotifyDexPcMoveEvents(), + Runtime::Current()->GetInstrumentation()->HasDexPcListeners()); + return shadow_frame.GetNotifyDexPcMoveEvents(); + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + return interpreter::NeedsMethodExitEvent(instrumentation); + } + + ALWAYS_INLINE WARN_UNUSED + static bool GetForcePopFrame(ShadowFrame& shadow_frame) { + DCHECK_IMPLIES(shadow_frame.GetForcePopFrame(), + Runtime::Current()->AreNonStandardExitsEnabled()); + return shadow_frame.GetForcePopFrame(); + } + + ALWAYS_INLINE + static void Branch(Thread* self, + ArtMethod* method, + uint32_t dex_pc, + int32_t dex_pc_offset, + const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + instrumentation->Branch(self, method, dex_pc, dex_pc_offset); + } + + // Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if + // the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able + // to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by + // jvmti-agents while handling breakpoint or single step events. We had to move this into its own + // function because it was making ExecuteSwitchImpl have too large a stack. + NO_INLINE static bool DoDexPcMoveEvent(Thread* self, + const CodeItemDataAccessor& accessor, + const ShadowFrame& shadow_frame, + uint32_t dex_pc, + const instrumentation::Instrumentation* instrumentation, + JValue* save_ref) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(instrumentation->HasDexPcListeners()); + StackHandleScope<2> hs(self); + Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException())); + mirror::Object* null_obj = nullptr; + HandleWrapper<mirror::Object> h( + hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); + self->ClearException(); + instrumentation->DexPcMovedEvent(self, + shadow_frame.GetThisObject(accessor.InsSize()), + shadow_frame.GetMethod(), + dex_pc); + if (UNLIKELY(self->IsExceptionPending())) { + // We got a new exception in the dex-pc-moved event. + // We just let this exception replace the old one. + // TODO It would be good to add the old exception to the + // suppressed exceptions of the new one if possible. + return false; // Pending exception. + } + if (UNLIKELY(!thr.IsNull())) { + self->SetException(thr.Get()); + } + return true; + } + + template <typename T> + ALWAYS_INLINE WARN_UNUSED + static bool SendMethodExitEvents( + Thread* self, + const instrumentation::Instrumentation* instrumentation, + ShadowFrame& frame, + ArtMethod* method, + T& result) REQUIRES_SHARED(Locks::mutator_lock_) { + return interpreter::SendMethodExitEvents(self, instrumentation, frame, method, result); + } +}; + // Explicit definition of ExecuteSwitchImplCpp. template HOT_ATTR void ExecuteSwitchImplCpp<false>(SwitchImplContext* ctx); diff --git a/runtime/interpreter/interpreter_switch_impl1.cc b/runtime/interpreter/interpreter_switch_impl1.cc index f0f38514db..2f24ab244c 100644 --- a/runtime/interpreter/interpreter_switch_impl1.cc +++ b/runtime/interpreter/interpreter_switch_impl1.cc @@ -115,6 +115,86 @@ void ActiveTransactionChecker::RecordArrayElementsInTransaction(ObjPtr<mirror::O } } +class InactiveInstrumentationHandler { + public: + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldReadListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!instrumentation->HasFieldReadListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasFieldWriteListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!instrumentation->HasFieldWriteListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool HasBranchListeners(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!instrumentation->HasBranchListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsDexPcEvents(ShadowFrame& shadow_frame) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!shadow_frame.GetNotifyDexPcMoveEvents()); + DCHECK(!Runtime::Current()->GetInstrumentation()->HasDexPcListeners()); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!interpreter::NeedsMethodExitEvent(instrumentation)); + return false; + } + + ALWAYS_INLINE WARN_UNUSED + static bool GetForcePopFrame(ShadowFrame& shadow_frame) { + DCHECK(!shadow_frame.GetForcePopFrame()); + DCHECK(!Runtime::Current()->AreNonStandardExitsEnabled()); + return false; + } + + NO_RETURN + static void Branch([[maybe_unused]] Thread* self, + [[maybe_unused]] ArtMethod* method, + [[maybe_unused]] uint32_t dex_pc, + [[maybe_unused]] int32_t dex_pc_offset, + [[maybe_unused]] const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } + + static bool DoDexPcMoveEvent( + [[maybe_unused]] Thread* self, + [[maybe_unused]] const CodeItemDataAccessor& accessor, + [[maybe_unused]] const ShadowFrame& shadow_frame, + [[maybe_unused]] uint32_t dex_pc, + [[maybe_unused]] const instrumentation::Instrumentation* instrumentation, + [[maybe_unused]] JValue* save_ref) + REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } + + template <typename T> + static bool SendMethodExitEvents( + [[maybe_unused]] Thread* self, + [[maybe_unused]] const instrumentation::Instrumentation* instrumentation, + [[maybe_unused]] ShadowFrame& frame, + [[maybe_unused]] ArtMethod* method, + [[maybe_unused]] T& result) REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } +}; + // Explicit definition of ExecuteSwitchImplCpp. template void ExecuteSwitchImplCpp<true>(SwitchImplContext* ctx); |