diff options
| -rw-r--r-- | compiler/oat_test.cc | 2 | ||||
| -rw-r--r-- | runtime/debugger.cc | 103 | ||||
| -rw-r--r-- | runtime/debugger.h | 39 | ||||
| -rw-r--r-- | runtime/instrumentation.h | 1 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_goto_table_impl.cc | 35 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_switch_impl.cc | 24 | ||||
| -rw-r--r-- | runtime/jdwp/jdwp_event.cc | 139 |
7 files changed, 246 insertions, 97 deletions
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 558ff1f3dc..ce35d0fae2 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -180,7 +180,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(80U, sizeof(OatHeader)); EXPECT_EQ(8U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(320U, sizeof(QuickEntryPoints)); + EXPECT_EQ(80 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c91b014b48..edf54be014 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -139,7 +139,7 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry, nullptr); + Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); } void MethodExited(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, @@ -149,7 +149,7 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit, &return_value); + Dbg::UpdateDebugger(thread, this_object, method, dex_pc, Dbg::kMethodExit, &return_value); } void MethodUnwind(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, @@ -163,7 +163,7 @@ 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); + Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, 0, nullptr); } void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, @@ -228,6 +228,15 @@ std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_; size_t Dbg::full_deoptimization_event_count_ = 0; size_t Dbg::delayed_full_undeoptimization_count_ = 0; +// Instrumentation event reference counters. +size_t Dbg::dex_pc_change_event_ref_count_ = 0; +size_t Dbg::method_enter_event_ref_count_ = 0; +size_t Dbg::method_exit_event_ref_count_ = 0; +size_t Dbg::field_read_event_ref_count_ = 0; +size_t Dbg::field_write_event_ref_count_ = 0; +size_t Dbg::exception_catch_event_ref_count_ = 0; +uint32_t Dbg::instrumentation_events_ = 0; + // Breakpoints. static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); @@ -641,14 +650,6 @@ bool Dbg::IsDisposed() { return gDisposed; } -// All the instrumentation events the debugger is registered for. -static constexpr uint32_t kListenerEvents = instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kDexPcMoved | - instrumentation::Instrumentation::kFieldRead | - instrumentation::Instrumentation::kFieldWritten | - instrumentation::Instrumentation::kExceptionCaught; - void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -668,6 +669,12 @@ void Dbg::GoActive() { CHECK_EQ(deoptimization_requests_.size(), 0U); CHECK_EQ(full_deoptimization_event_count_, 0U); CHECK_EQ(delayed_full_undeoptimization_count_, 0U); + CHECK_EQ(dex_pc_change_event_ref_count_, 0U); + CHECK_EQ(method_enter_event_ref_count_, 0U); + CHECK_EQ(method_exit_event_ref_count_, 0U); + CHECK_EQ(field_read_event_ref_count_, 0U); + CHECK_EQ(field_write_event_ref_count_, 0U); + CHECK_EQ(exception_catch_event_ref_count_, 0U); } Runtime* runtime = Runtime::Current(); @@ -676,7 +683,7 @@ void Dbg::GoActive() { ThreadState old_state = self->SetStateUnsafe(kRunnable); CHECK_NE(old_state, kRunnable); runtime->GetInstrumentation()->EnableDeoptimization(); - runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, kListenerEvents); + instrumentation_events_ = 0; gDebuggerActive = true; CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); runtime->GetThreadList()->ResumeAll(); @@ -708,7 +715,11 @@ void Dbg::Disconnected() { full_deoptimization_event_count_ = 0U; delayed_full_undeoptimization_count_ = 0U; } - runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, kListenerEvents); + if (instrumentation_events_ != 0) { + runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, + instrumentation_events_); + instrumentation_events_ = 0; + } runtime->GetInstrumentation()->DisableDeoptimization(); gDebuggerActive = false; } @@ -2586,13 +2597,12 @@ void Dbg::PostClassPrepare(mirror::Class* c) { } void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* m, uint32_t dex_pc) { + mirror::ArtMethod* m, uint32_t dex_pc, + int event_flags, const JValue* return_value) { if (!IsDebuggerActive() || dex_pc == static_cast<uint32_t>(-2) /* fake method exit */) { return; } - int event_flags = 0; - if (IsBreakpoint(m, dex_pc)) { event_flags |= kBreakpoint; } @@ -2660,7 +2670,26 @@ void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, // If there's something interesting going on, see if it matches one // of the debugger filters. if (event_flags != 0) { - Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags, nullptr); + Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags, return_value); + } +} + +size_t* Dbg::GetReferenceCounterForEvent(uint32_t instrumentation_event) { + switch (instrumentation_event) { + case instrumentation::Instrumentation::kMethodEntered: + return &method_enter_event_ref_count_; + case instrumentation::Instrumentation::kMethodExited: + return &method_exit_event_ref_count_; + case instrumentation::Instrumentation::kDexPcMoved: + return &dex_pc_change_event_ref_count_; + case instrumentation::Instrumentation::kFieldRead: + return &field_read_event_ref_count_; + case instrumentation::Instrumentation::kFieldWritten: + return &field_write_event_ref_count_; + case instrumentation::Instrumentation::kExceptionCaught: + return &exception_catch_event_ref_count_; + default: + return nullptr; } } @@ -2671,6 +2700,19 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { case DeoptimizationRequest::kNothing: LOG(WARNING) << "Ignoring empty deoptimization request."; break; + case DeoptimizationRequest::kRegisterForEvent: + VLOG(jdwp) << StringPrintf("Add debugger as listener for instrumentation event 0x%x", + request.instrumentation_event); + instrumentation->AddListener(&gDebugInstrumentationListener, request.instrumentation_event); + instrumentation_events_ |= request.instrumentation_event; + break; + case DeoptimizationRequest::kUnregisterForEvent: + VLOG(jdwp) << StringPrintf("Remove debugger as listener for instrumentation event 0x%x", + request.instrumentation_event); + instrumentation->RemoveListener(&gDebugInstrumentationListener, + request.instrumentation_event); + instrumentation_events_ &= ~request.instrumentation_event; + break; case DeoptimizationRequest::kFullDeoptimization: VLOG(jdwp) << "Deoptimize the world ..."; instrumentation->DeoptimizeEverything(); @@ -2729,6 +2771,32 @@ void Dbg::RequestDeoptimization(const DeoptimizationRequest& req) { void Dbg::RequestDeoptimizationLocked(const DeoptimizationRequest& req) { switch (req.kind) { + case DeoptimizationRequest::kRegisterForEvent: { + DCHECK_NE(req.instrumentation_event, 0u); + size_t* counter = GetReferenceCounterForEvent(req.instrumentation_event); + CHECK(counter != nullptr) << StringPrintf("No counter for instrumentation event 0x%x", + req.instrumentation_event); + if (*counter == 0) { + VLOG(jdwp) << StringPrintf("Queue request #%zd to start listening to instrumentation event 0x%x", + deoptimization_requests_.size(), req.instrumentation_event); + deoptimization_requests_.push_back(req); + } + *counter = *counter + 1; + break; + } + case DeoptimizationRequest::kUnregisterForEvent: { + DCHECK_NE(req.instrumentation_event, 0u); + size_t* counter = GetReferenceCounterForEvent(req.instrumentation_event); + CHECK(counter != nullptr) << StringPrintf("No counter for instrumentation event 0x%x", + req.instrumentation_event); + *counter = *counter - 1; + if (*counter == 0) { + VLOG(jdwp) << StringPrintf("Queue request #%zd to stop listening to instrumentation event 0x%x", + deoptimization_requests_.size(), req.instrumentation_event); + deoptimization_requests_.push_back(req); + } + break; + } case DeoptimizationRequest::kFullDeoptimization: { DCHECK(req.method == nullptr); if (full_deoptimization_event_count_ == 0) { @@ -2741,7 +2809,6 @@ void Dbg::RequestDeoptimizationLocked(const DeoptimizationRequest& req) { } case DeoptimizationRequest::kFullUndeoptimization: { DCHECK(req.method == nullptr); - DCHECK_GT(full_deoptimization_event_count_, 0U); --full_deoptimization_event_count_; if (full_deoptimization_event_count_ == 0) { VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() diff --git a/runtime/debugger.h b/runtime/debugger.h index bef708cdc3..31ffd6e4e5 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -129,21 +129,31 @@ struct SingleStepControl { DISALLOW_COPY_AND_ASSIGN(SingleStepControl); }; +// TODO rename to InstrumentationRequest. struct DeoptimizationRequest { enum Kind { kNothing, // no action. + kRegisterForEvent, // start listening for instrumentation event. + kUnregisterForEvent, // stop listening for instrumentation event. kFullDeoptimization, // deoptimize everything. kFullUndeoptimization, // undeoptimize everything. kSelectiveDeoptimization, // deoptimize one method. kSelectiveUndeoptimization // undeoptimize one method. }; - DeoptimizationRequest() : kind(kNothing), method(nullptr) {} + DeoptimizationRequest() : kind(kNothing), instrumentation_event(0), method(nullptr) {} void VisitRoots(RootCallback* callback, void* arg); Kind kind; + // TODO we could use a union to hold the instrumentation_event and the method since they + // respectively have sense only for kRegisterForEvent/kUnregisterForEvent and + // kSelectiveDeoptimization/kSelectiveUndeoptimization. + + // Event to start or stop listening to. Only for kRegisterForEvent and kUnregisterForEvent. + uint32_t instrumentation_event; + // Method for selective deoptimization. mirror::ArtMethod* method; }; @@ -417,10 +427,6 @@ class Dbg { kMethodEntry = 0x04, kMethodExit = 0x08, }; - static void PostLocationEvent(mirror::ArtMethod* method, int pcOffset, - mirror::Object* thisPtr, int eventFlags, - const JValue* return_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void PostFieldAccessEvent(mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object, mirror::ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -439,7 +445,8 @@ class Dbg { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void UpdateDebugger(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t new_dex_pc) + mirror::ArtMethod* method, uint32_t new_dex_pc, + int event_flags, const JValue* return_value) LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -561,6 +568,11 @@ class Dbg { static void PostThreadStartOrStop(Thread*, uint32_t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void PostLocationEvent(mirror::ArtMethod* method, int pcOffset, + mirror::Object* thisPtr, int eventFlags, + const JValue* return_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static JDWP::ObjectId GetThisObjectIdForEvent(mirror::Object* this_object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -579,11 +591,13 @@ class Dbg { static size_t alloc_record_count_ GUARDED_BY(alloc_tracker_lock_); // Guards deoptimization requests. + // TODO rename to instrumentation_update_lock. static Mutex* deoptimization_lock_ ACQUIRED_AFTER(Locks::breakpoint_lock_); // Deoptimization requests to be processed each time the event list is updated. This is used when // registering and unregistering events so we do not deoptimize while holding the event list // lock. + // TODO rename to instrumentation_requests. static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(deoptimization_lock_); // Count the number of events requiring full deoptimization. When the counter is > 0, everything @@ -596,6 +610,19 @@ class Dbg { // session. static size_t delayed_full_undeoptimization_count_ GUARDED_BY(deoptimization_lock_); + static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event); + + // Instrumentation event reference counters. + // TODO we could use an array instead of having all these dedicated counters. Instrumentation + // events are bits of a mask so we could convert them to array index. + static size_t dex_pc_change_event_ref_count_ GUARDED_BY(deoptimization_lock_); + static size_t method_enter_event_ref_count_ GUARDED_BY(deoptimization_lock_); + static size_t method_exit_event_ref_count_ GUARDED_BY(deoptimization_lock_); + static size_t field_read_event_ref_count_ GUARDED_BY(deoptimization_lock_); + static size_t field_write_event_ref_count_ GUARDED_BY(deoptimization_lock_); + static size_t exception_catch_event_ref_count_ GUARDED_BY(deoptimization_lock_); + static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_); + DISALLOW_COPY_AND_ASSIGN(Dbg); }; diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 3de07283b5..5630862564 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -238,6 +238,7 @@ class Instrumentation { bool IsActive() const { return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ || + have_field_read_listeners_ || have_field_write_listeners_ || have_exception_caught_listeners_ || have_method_unwind_listeners_; } diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index e425e9132e..74f386c922 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -145,12 +145,14 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* 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.. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); + notified_method_entry_event = true; } } @@ -2384,16 +2386,29 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } } - // Create alternative instruction handlers dedicated to instrumentation. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ - alt_op_##code: { \ - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \ - if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ - shadow_frame.GetMethod(), dex_pc); \ - } \ - UPDATE_HANDLER_TABLE(); \ - goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \ +// Create alternative instruction handlers dedicated to instrumentation. +// Return instructions must not call Instrumentation::DexPcMovedEvent since they already call +// Instrumentation::MethodExited. This is to avoid posting debugger events twice for this location. +#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_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]; \ } #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 9c13973bd7..97c216dc07 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -48,11 +48,20 @@ namespace interpreter { } while (false) // Code to run before each dex instruction. -#define PREAMBLE() +#define PREAMBLE() \ + do { \ + DCHECK(!inst->IsReturn()); \ + if (UNLIKELY(notified_method_entry_event)) { \ + notified_method_entry_event = false; \ + } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ + shadow_frame.GetMethod(), dex_pc); \ + } \ + } while (false) template<bool do_access_check, bool transaction_active> JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) { + ShadowFrame& shadow_frame, JValue result_register) { bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; @@ -61,11 +70,13 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem self->VerifyStack(); uint32_t dex_pc = shadow_frame.GetDexPC(); + bool notified_method_entry_event = false; const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. 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_; @@ -74,10 +85,6 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem while (true) { dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), dex_pc); - } TraceExecution(shadow_frame, inst, dex_pc, mh); inst_data = inst->Fetch16(0); switch (inst->Opcode(inst_data)) { @@ -163,7 +170,6 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem break; } case Instruction::RETURN_VOID: { - PREAMBLE(); JValue result; if (do_access_check) { // If access checks are required then the dex-to-dex compiler and analysis of @@ -182,7 +188,6 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem return result; } case Instruction::RETURN_VOID_BARRIER: { - PREAMBLE(); QuasiAtomic::MembarStoreLoad(); JValue result; if (UNLIKELY(self->TestAllFlags())) { @@ -196,7 +201,6 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem return result; } case Instruction::RETURN: { - PREAMBLE(); JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); @@ -211,7 +215,6 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem return result; } case Instruction::RETURN_WIDE: { - PREAMBLE(); JValue result; result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { @@ -225,7 +228,6 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem return result; } case Instruction::RETURN_OBJECT: { - PREAMBLE(); JValue result; if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 4e2b0f813c..cb2c420dbb 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -136,6 +136,28 @@ static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { } } +uint32_t GetInstrumentationEventFor(JdwpEventKind eventKind) { + switch (eventKind) { + case EK_BREAKPOINT: + case EK_SINGLE_STEP: + return instrumentation::Instrumentation::kDexPcMoved; + case EK_EXCEPTION: + case EK_EXCEPTION_CATCH: + return instrumentation::Instrumentation::kExceptionCaught; + case EK_METHOD_ENTRY: + return instrumentation::Instrumentation::kMethodEntered; + case EK_METHOD_EXIT: + case EK_METHOD_EXIT_WITH_RETURN_VALUE: + return instrumentation::Instrumentation::kMethodExited; + case EK_FIELD_ACCESS: + return instrumentation::Instrumentation::kFieldRead; + case EK_FIELD_MODIFICATION: + return instrumentation::Instrumentation::kFieldWritten; + default: + return 0; + } +} + /* * Add an event to the list. Ordering is not important. * @@ -148,30 +170,40 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { CHECK(pEvent->prev == NULL); CHECK(pEvent->next == NULL); - /* - * If one or more "break"-type mods are used, register them with - * the interpreter. - */ - DeoptimizationRequest req; - for (int i = 0; i < pEvent->modCount; i++) { - const JdwpEventMod* pMod = &pEvent->mods[i]; - if (pMod->modKind == MK_LOCATION_ONLY) { - /* should only be for Breakpoint, Step, and Exception */ - Dbg::WatchLocation(&pMod->locationOnly.loc, &req); - } else if (pMod->modKind == MK_STEP) { - /* should only be for EK_SINGLE_STEP; should only be one */ - JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size); - JdwpStepDepth depth = static_cast<JdwpStepDepth>(pMod->step.depth); - JdwpError status = Dbg::ConfigureStep(pMod->step.threadId, size, depth); - if (status != ERR_NONE) { - return status; + { + /* + * If one or more "break"-type mods are used, register them with + * the interpreter. + */ + DeoptimizationRequest req; + for (int i = 0; i < pEvent->modCount; i++) { + const JdwpEventMod* pMod = &pEvent->mods[i]; + if (pMod->modKind == MK_LOCATION_ONLY) { + /* should only be for Breakpoint, Step, and Exception */ + Dbg::WatchLocation(&pMod->locationOnly.loc, &req); + } else if (pMod->modKind == MK_STEP) { + /* should only be for EK_SINGLE_STEP; should only be one */ + JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size); + JdwpStepDepth depth = static_cast<JdwpStepDepth>(pMod->step.depth); + JdwpError status = Dbg::ConfigureStep(pMod->step.threadId, size, depth); + if (status != ERR_NONE) { + return status; + } } } + if (NeedsFullDeoptimization(pEvent->eventKind)) { + CHECK_EQ(req.kind, DeoptimizationRequest::kNothing); + CHECK(req.method == nullptr); + req.kind = DeoptimizationRequest::kFullDeoptimization; + } + Dbg::RequestDeoptimization(req); } - if (NeedsFullDeoptimization(pEvent->eventKind)) { - CHECK_EQ(req.kind, DeoptimizationRequest::kNothing); - CHECK(req.method == nullptr); - req.kind = DeoptimizationRequest::kFullDeoptimization; + uint32_t instrumentation_event = GetInstrumentationEventFor(pEvent->eventKind); + if (instrumentation_event != 0) { + DeoptimizationRequest req; + req.kind = DeoptimizationRequest::kRegisterForEvent; + req.instrumentation_event = instrumentation_event; + Dbg::RequestDeoptimization(req); } { @@ -187,9 +219,6 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { ++event_list_size_; } - // TODO we can do better job here since we should process only one request: the one we just - // created. - Dbg::RequestDeoptimization(req); Dbg::ManageDeoptimization(); return ERR_NONE; @@ -219,40 +248,48 @@ void JdwpState::UnregisterEvent(JdwpEvent* pEvent) { } pEvent->prev = NULL; - /* - * Unhook us from the interpreter, if necessary. - */ - DeoptimizationRequest req; - for (int i = 0; i < pEvent->modCount; i++) { - JdwpEventMod* pMod = &pEvent->mods[i]; - if (pMod->modKind == MK_LOCATION_ONLY) { - /* should only be for Breakpoint, Step, and Exception */ - Dbg::UnwatchLocation(&pMod->locationOnly.loc, &req); + { + /* + * Unhook us from the interpreter, if necessary. + */ + DeoptimizationRequest req; + for (int i = 0; i < pEvent->modCount; i++) { + JdwpEventMod* pMod = &pEvent->mods[i]; + if (pMod->modKind == MK_LOCATION_ONLY) { + /* should only be for Breakpoint, Step, and Exception */ + Dbg::UnwatchLocation(&pMod->locationOnly.loc, &req); + } + if (pMod->modKind == MK_STEP) { + /* should only be for EK_SINGLE_STEP; should only be one */ + Dbg::UnconfigureStep(pMod->step.threadId); + } } - if (pMod->modKind == MK_STEP) { - /* should only be for EK_SINGLE_STEP; should only be one */ - Dbg::UnconfigureStep(pMod->step.threadId); + if (pEvent->eventKind == EK_SINGLE_STEP) { + // Special case for single-steps where we want to avoid the slow pattern deoptimize/undeoptimize + // loop between each single-step. In a IDE, this would happens each time the user click on the + // "single-step" button. Here we delay the full undeoptimization to the next resume + // (VM.Resume or ThreadReference.Resume) or the end of the debugging session (VM.Dispose or + // runtime shutdown). + // Therefore, in a singles-stepping sequence, only the first single-step will trigger a full + // deoptimization and only the last single-step will trigger a full undeoptimization. + Dbg::DelayFullUndeoptimization(); + } else if (NeedsFullDeoptimization(pEvent->eventKind)) { + CHECK_EQ(req.kind, DeoptimizationRequest::kNothing); + CHECK(req.method == nullptr); + req.kind = DeoptimizationRequest::kFullUndeoptimization; } + Dbg::RequestDeoptimization(req); } - if (pEvent->eventKind == EK_SINGLE_STEP) { - // Special case for single-steps where we want to avoid the slow pattern deoptimize/undeoptimize - // loop between each single-step. In a IDE, this would happens each time the user click on the - // "single-step" button. Here we delay the full undeoptimization to the next resume - // (VM.Resume or ThreadReference.Resume) or the end of the debugging session (VM.Dispose or - // runtime shutdown). - // Therefore, in a singles-stepping sequence, only the first single-step will trigger a full - // deoptimization and only the last single-step will trigger a full undeoptimization. - Dbg::DelayFullUndeoptimization(); - } else if (NeedsFullDeoptimization(pEvent->eventKind)) { - CHECK_EQ(req.kind, DeoptimizationRequest::kNothing); - CHECK(req.method == nullptr); - req.kind = DeoptimizationRequest::kFullUndeoptimization; + uint32_t instrumentation_event = GetInstrumentationEventFor(pEvent->eventKind); + if (instrumentation_event != 0) { + DeoptimizationRequest req; + req.kind = DeoptimizationRequest::kUnregisterForEvent; + req.instrumentation_event = instrumentation_event; + Dbg::RequestDeoptimization(req); } --event_list_size_; CHECK(event_list_size_ != 0 || event_list_ == NULL); - - Dbg::RequestDeoptimization(req); } /* |