diff options
-rw-r--r-- | runtime/asm_support.h | 2 | ||||
-rw-r--r-- | runtime/debugger.cc | 76 | ||||
-rw-r--r-- | runtime/debugger.h | 4 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_goto_table_impl.cc | 47 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl.cc | 27 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_event.cc | 2 | ||||
-rw-r--r-- | runtime/oat.h | 2 | ||||
-rw-r--r-- | runtime/thread.h | 18 |
8 files changed, 107 insertions, 71 deletions
diff --git a/runtime/asm_support.h b/runtime/asm_support.h index a115fbe..de4783a 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -89,7 +89,7 @@ ADD_TEST_EQ(THREAD_ID_OFFSET, art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.card_table. -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 128 ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET, art::Thread::CardTableOffset<__SIZEOF_POINTER__>().Int32Value()) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index dc1b4f1..47371e5 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -232,13 +232,29 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati virtual ~DebugInstrumentationListener() {} void MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, - uint32_t dex_pc ATTRIBUTE_UNUSED) + uint32_t dex_pc) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (method->IsNative()) { // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); + if (IsListeningToDexPcMoved()) { + // We also listen to kDexPcMoved instrumentation event so we know the DexPcMoved method is + // going to be called right after us. To avoid sending JDWP events twice for this location, + // we report the event in DexPcMoved. However, we must remind this is method entry so we + // send the METHOD_ENTRY event. And we can also group it with other events for this location + // like BREAKPOINT or SINGLE_STEP (or even METHOD_EXIT if this is a RETURN instruction). + thread->SetDebugMethodEntry(); + } else if (IsListeningToMethodExit() && IsReturn(method, dex_pc)) { + // We also listen to kMethodExited instrumentation event and the current instruction is a + // RETURN so we know the MethodExited method is going to be called right after us. To avoid + // sending JDWP events twice for this location, we report the event(s) in MethodExited. + // However, we must remind this is method entry so we send the METHOD_ENTRY event. And we can + // also group it with other events for this location like BREAKPOINT or SINGLE_STEP. + thread->SetDebugMethodEntry(); + } else { + Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); + } } void MethodExited(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, @@ -248,14 +264,20 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::UpdateDebugger(thread, this_object, method, dex_pc, Dbg::kMethodExit, &return_value); + uint32_t events = Dbg::kMethodExit; + if (thread->IsDebugMethodEntry()) { + // It is also the method entry. + DCHECK(IsReturn(method, dex_pc)); + events |= Dbg::kMethodEntry; + thread->ClearDebugMethodEntry(); + } + Dbg::UpdateDebugger(thread, this_object, method, dex_pc, events, &return_value); } - void MethodUnwind(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, - uint32_t dex_pc) + void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object ATTRIBUTE_UNUSED, + mirror::ArtMethod* method, uint32_t dex_pc) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // We're not recorded to listen to this kind of event, so complain. - UNUSED(thread, this_object, method, dex_pc); LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method) << " " << dex_pc; } @@ -263,13 +285,27 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati void DexPcMoved(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, uint32_t new_dex_pc) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, 0, nullptr); + if (IsListeningToMethodExit() && IsReturn(method, new_dex_pc)) { + // We also listen to kMethodExited instrumentation event and the current instruction is a + // RETURN so we know the MethodExited method is going to be called right after us. Like in + // MethodEntered, we delegate event reporting to MethodExited. + // Besides, if this RETURN instruction is the only one in the method, we can send multiple + // JDWP events in the same packet: METHOD_ENTRY, METHOD_EXIT, BREAKPOINT and/or SINGLE_STEP. + // Therefore, we must not clear the debug method entry flag here. + } else { + uint32_t events = 0; + if (thread->IsDebugMethodEntry()) { + // It is also the method entry. + events = Dbg::kMethodEntry; + thread->ClearDebugMethodEntry(); + } + Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, events, nullptr); + } } - void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, - uint32_t dex_pc, ArtField* field) + void FieldRead(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, ArtField* field) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - UNUSED(thread); Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field); } @@ -293,6 +329,26 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati } private: + static bool IsReturn(mirror::ArtMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::CodeItem* code_item = method->GetCodeItem(); + const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]); + return instruction->IsReturn(); + } + + static bool IsListeningToDexPcMoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsListeningTo(instrumentation::Instrumentation::kDexPcMoved); + } + + static bool IsListeningToMethodExit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsListeningTo(instrumentation::Instrumentation::kMethodExited); + } + + static bool IsListeningTo(instrumentation::Instrumentation::InstrumentationEvent event) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (Dbg::GetInstrumentationEvents() & event) != 0; + } + DISALLOW_COPY_AND_ASSIGN(DebugInstrumentationListener); } gDebugInstrumentationListener; diff --git a/runtime/debugger.h b/runtime/debugger.h index fe90eb6..789a0a4 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -714,6 +714,10 @@ class Dbg { static JDWP::JdwpState* GetJdwpState(); + static uint32_t GetInstrumentationEvents() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return instrumentation_events_; + } + private: static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa, int slot, diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 878efba..dd1f55e 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -156,7 +156,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); uint16_t inst_data; const void* const* currentHandlersTable; - bool notified_method_entry_event = false; UPDATE_HANDLER_TABLE(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing. if (kIsDebugBuild) { @@ -166,7 +165,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); - notified_method_entry_event = true; } } @@ -264,9 +262,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -281,9 +276,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -299,9 +291,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -316,9 +305,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -352,9 +338,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } @@ -2510,26 +2493,16 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF // Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The // compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to // a constant condition that would remove the "if" statement so the test is free. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ - alt_op_##code: { \ - if (Instruction::code != Instruction::RETURN_VOID && \ - Instruction::code != Instruction::RETURN_VOID_NO_BARRIER && \ - Instruction::code != Instruction::RETURN && \ - Instruction::code != Instruction::RETURN_WIDE && \ - Instruction::code != Instruction::RETURN_OBJECT) { \ - if (LIKELY(!notified_method_entry_event)) { \ - Runtime* runtime = Runtime::Current(); \ - const instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); \ - if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ - Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \ - instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); \ - } \ - } else { \ - notified_method_entry_event = false; \ - } \ - } \ - UPDATE_HANDLER_TABLE(); \ - goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \ +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ + alt_op_##code: { \ + Runtime* const runtime = Runtime::Current(); \ + const instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \ + instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); \ + } \ + UPDATE_HANDLER_TABLE(); \ + goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \ } #include "dex_instruction_list.h" DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER) diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index a5e5299..0e3420f 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -47,10 +47,7 @@ namespace interpreter { // Code to run before each dex instruction. #define PREAMBLE() \ do { \ - DCHECK(!inst->IsReturn()); \ - if (UNLIKELY(notified_method_entry_event)) { \ - notified_method_entry_event = false; \ - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ shadow_frame.GetMethod(), dex_pc); \ } \ @@ -67,7 +64,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, self->VerifyStack(); uint32_t dex_pc = shadow_frame.GetDexPC(); - bool notified_method_entry_event = false; const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing. if (kIsDebugBuild) { @@ -76,7 +72,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); - notified_method_entry_event = true; } } const uint16_t* const insns = code_item->insns_; @@ -171,19 +166,18 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, break; } case Instruction::RETURN_VOID_NO_BARRIER: { + PREAMBLE(); JValue result; self->AllowThreadSuspension(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN_VOID: { + PREAMBLE(); QuasiAtomic::ThreadFenceForConstructor(); JValue result; self->AllowThreadSuspension(); @@ -191,13 +185,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN: { + PREAMBLE(); JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); @@ -206,13 +198,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN_WIDE: { + PREAMBLE(); JValue result; result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); self->AllowThreadSuspension(); @@ -220,13 +210,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } case Instruction::RETURN_OBJECT: { + PREAMBLE(); JValue result; self->AllowThreadSuspension(); const size_t ref_idx = inst->VRegA_11x(inst_data); @@ -254,9 +242,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); - } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); } return result; } diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 1ec800f..ab3f2e4 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -141,6 +141,8 @@ static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { } } +// Returns the instrumentation event the DebugInstrumentationListener must +// listen to in order to properly report the given JDWP event to the debugger. static uint32_t GetInstrumentationEventFor(JdwpEventKind eventKind) { switch (eventKind) { case EK_BREAKPOINT: diff --git a/runtime/oat.h b/runtime/oat.h index a31e09a..aaf442a 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '6', '1', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '6', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/thread.h b/runtime/thread.h index e766daa..9346813 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -752,6 +752,18 @@ class Thread { tls32_.ready_for_debug_invoke = ready; } + bool IsDebugMethodEntry() const { + return tls32_.debug_method_entry_; + } + + void SetDebugMethodEntry() { + tls32_.debug_method_entry_ = true; + } + + void ClearDebugMethodEntry() { + tls32_.debug_method_entry_ = false; + } + // Activates single step control for debugging. The thread takes the // ownership of the given SingleStepControl*. It is deleted by a call // to DeactivateSingleStepControl or upon thread destruction. @@ -1028,7 +1040,7 @@ class Thread { 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), handling_signal_(false), suspended_at_suspend_check(false), - ready_for_debug_invoke(false) { + ready_for_debug_invoke(false), debug_method_entry_(false) { } union StateAndFlags state_and_flags; @@ -1077,6 +1089,10 @@ class Thread { // used to invoke method from the debugger which is only allowed when // the thread is suspended by an event. bool32_t ready_for_debug_invoke; + + // True if the thread enters a method. This is used to detect method entry + // event for the debugger. + bool32_t debug_method_entry_; } tls32_; struct PACKED(8) tls_64bit_sized_values { |