From 3f52eafe5577b8489f90dc8ed5981b3455206147 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 4 Apr 2014 17:50:18 +0200 Subject: [PATCH] Prepare field watchpoint support Adds field read/write events in the instrumentation. The debugger now registers as a listener for these events so JDWP field access and field modification events can be reported. This CL will be followed by another one to report these events from the interpreter. Therefore no JDWP field access and field modification events can be sent for now. Bug: 8267708 Change-Id: If2a93eb590805567d69015c83cce9cd2ab712cbd --- runtime/debugger.cc | 146 ++++++++++++++++++++++++++++++++----------- runtime/debugger.h | 17 ++++- runtime/instrumentation.cc | 49 +++++++++++++++ runtime/instrumentation.h | 67 ++++++++++++++++++-- runtime/jdwp/jdwp.h | 11 ++++ runtime/jdwp/jdwp_event.cc | 99 ++++++++++++++++++++++++----- runtime/jdwp/jdwp_handler.cc | 4 +- runtime/trace.cc | 15 +++++ runtime/trace.h | 45 +++++++------ 9 files changed, 371 insertions(+), 82 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 514ad4cb3..6dd2493e2 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -126,14 +126,14 @@ static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs) return os; } -class DebugInstrumentationListener : public instrumentation::InstrumentationListener { +class DebugInstrumentationListener FINAL : public instrumentation::InstrumentationListener { public: DebugInstrumentationListener() {} virtual ~DebugInstrumentationListener() {} - virtual void MethodEntered(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + 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; @@ -141,10 +141,9 @@ class DebugInstrumentationListener : public instrumentation::InstrumentationList Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry, nullptr); } - virtual void MethodExited(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, - uint32_t dex_pc, const JValue& return_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void MethodExited(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, const JValue& return_value) + 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; @@ -152,26 +151,41 @@ class DebugInstrumentationListener : public instrumentation::InstrumentationList Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit, &return_value); } - virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void MethodUnwind(Thread* thread, mirror::Object* this_object, 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. LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method) << " " << dex_pc; } - virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t new_dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + 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); } - virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, - mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, - mirror::Throwable* exception_object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Dbg::PostException(thread, throw_location, catch_method, catch_dex_pc, exception_object); + void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, mirror::ArtField* field) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field); + } + + void FieldWritten(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, mirror::ArtField* field, const JValue& field_value) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostFieldModificationEvent(method, dex_pc, this_object, field, &field_value); + } + + void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostException(throw_location, catch_method, catch_dex_pc, exception_object); } + + private: + DISALLOW_COPY_AND_ASSIGN(DebugInstrumentationListener); } gDebugInstrumentationListener; // JDWP is allowed unless the Zygote forbids it. @@ -607,6 +621,14 @@ 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. @@ -633,11 +655,7 @@ void Dbg::GoActive() { ThreadState old_state = self->SetStateUnsafe(kRunnable); CHECK_NE(old_state, kRunnable); runtime->GetInstrumentation()->EnableDeoptimization(); - runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, - instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kDexPcMoved | - instrumentation::Instrumentation::kExceptionCaught); + runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, kListenerEvents); gDebuggerActive = true; CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); runtime->GetThreadList()->ResumeAll(); @@ -668,11 +686,7 @@ void Dbg::Disconnected() { deoptimization_requests_.clear(); full_deoptimization_event_count_ = 0U; } - runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, - instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kDexPcMoved | - instrumentation::Instrumentation::kExceptionCaught); + runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, kListenerEvents); runtime->GetInstrumentation()->DisableDeoptimization(); gDebuggerActive = false; } @@ -1572,6 +1586,13 @@ void Dbg::OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return OutputJValue(tag, return_value, pReply); } +void Dbg::OutputFieldValue(JDWP::FieldId field_id, const JValue* field_value, + JDWP::ExpandBuf* pReply) { + mirror::ArtField* f = FromFieldId(field_id); + JDWP::JdwpTag tag = BasicTagFromDescriptor(FieldHelper(f).GetTypeDescriptor()); + OutputJValue(tag, field_value, pReply); +} + JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id, std::vector& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -2444,21 +2465,70 @@ JDWP::JdwpError Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame return visitor.error_; } +JDWP::ObjectId Dbg::GetThisObjectIdForEvent(mirror::Object* this_object) { + // If 'this_object' isn't already in the registry, we know that we're not looking for it, so + // there's no point adding it to the registry and burning through ids. + // When registering an event request with an instance filter, we've been given an existing object + // id so it must already be present in the registry when the event fires. + JDWP::ObjectId this_id = 0; + if (this_object != nullptr && gRegistry->Contains(this_object)) { + this_id = gRegistry->Add(this_object); + } + return this_id; +} + void Dbg::PostLocationEvent(mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object, int event_flags, const JValue* return_value) { + if (!IsDebuggerActive()) { + return; + } + DCHECK(m != nullptr); + DCHECK_EQ(m->IsStatic(), this_object == nullptr); JDWP::JdwpLocation location; SetLocation(location, m, dex_pc); - // If 'this_object' isn't already in the registry, we know that we're not looking for it, - // so there's no point adding it to the registry and burning through ids. - JDWP::ObjectId this_id = 0; - if (gRegistry->Contains(this_object)) { - this_id = gRegistry->Add(this_object); - } + // We need 'this' for InstanceOnly filters only. + JDWP::ObjectId this_id = GetThisObjectIdForEvent(this_object); gJdwpState->PostLocationEvent(&location, this_id, event_flags, return_value); } -void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location, +void Dbg::PostFieldAccessEvent(mirror::ArtMethod* m, int dex_pc, + mirror::Object* this_object, mirror::ArtField* f) { + if (!IsDebuggerActive()) { + return; + } + DCHECK(m != nullptr); + DCHECK(f != nullptr); + JDWP::JdwpLocation location; + SetLocation(location, m, dex_pc); + + JDWP::RefTypeId type_id = gRegistry->AddRefType(f->GetDeclaringClass()); + JDWP::FieldId field_id = ToFieldId(f); + JDWP::ObjectId this_id = gRegistry->Add(this_object); + + gJdwpState->PostFieldEvent(&location, type_id, field_id, this_id, nullptr, false); +} + +void Dbg::PostFieldModificationEvent(mirror::ArtMethod* m, int dex_pc, + mirror::Object* this_object, mirror::ArtField* f, + const JValue* field_value) { + if (!IsDebuggerActive()) { + return; + } + DCHECK(m != nullptr); + DCHECK(f != nullptr); + DCHECK(field_value != nullptr); + JDWP::JdwpLocation location; + SetLocation(location, m, dex_pc); + + JDWP::RefTypeId type_id = gRegistry->AddRefType(f->GetDeclaringClass()); + JDWP::FieldId field_id = ToFieldId(f); + JDWP::ObjectId this_id = gRegistry->Add(this_object); + + gJdwpState->PostFieldEvent(&location, type_id, field_id, this_id, field_value, true); +} + +void Dbg::PostException(const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, mirror::Throwable* exception_object) { if (!IsDebuggerActive()) { @@ -2470,8 +2540,8 @@ void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location, JDWP::JdwpLocation catch_location; SetLocation(catch_location, catch_method, catch_dex_pc); - // We need 'this' for InstanceOnly filters. - JDWP::ObjectId this_id = gRegistry->Add(throw_location.GetThis()); + // We need 'this' for InstanceOnly filters only. + JDWP::ObjectId this_id = GetThisObjectIdForEvent(throw_location.GetThis()); JDWP::ObjectId exception_id = gRegistry->Add(exception_object); JDWP::RefTypeId exception_class_id = gRegistry->AddRefType(exception_object->GetClass()); diff --git a/runtime/debugger.h b/runtime/debugger.h index 23c9c6a1a..6126ddc2a 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -35,6 +35,7 @@ namespace art { namespace mirror { +class ArtField; class ArtMethod; class Class; class Object; @@ -297,6 +298,9 @@ class Dbg { static void OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value, JDWP::ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void OutputFieldValue(JDWP::FieldId field_id, const JValue* field_value, + JDWP::ExpandBuf* pReply) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::JdwpError GetBytecodes(JDWP::RefTypeId class_id, JDWP::MethodId method_id, std::vector& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -411,8 +415,14 @@ class Dbg { mirror::Object* thisPtr, int eventFlags, const JValue* return_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void PostException(Thread* thread, const ThrowLocation& throw_location, - mirror::ArtMethod* catch_method, + static void PostFieldAccessEvent(mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object, + mirror::ArtField* f) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void PostFieldModificationEvent(mirror::ArtMethod* m, int dex_pc, + mirror::Object* this_object, mirror::ArtField* f, + const JValue* field_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void PostException(const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, mirror::Throwable* exception) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void PostThreadStart(Thread* t) @@ -538,6 +548,9 @@ class Dbg { static void PostThreadStartOrStop(Thread*, uint32_t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static JDWP::ObjectId GetThisObjectIdForEvent(mirror::Object* this_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void ProcessDeoptimizationRequest(const DeoptimizationRequest& request) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 525e2b30d..bcde9e5a2 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -63,6 +63,7 @@ Instrumentation::Instrumentation() interpret_only_(false), forced_interpret_only_(false), have_method_entry_listeners_(false), have_method_exit_listeners_(false), have_method_unwind_listeners_(false), have_dex_pc_listeners_(false), + have_field_read_listeners_(false), have_field_write_listeners_(false), have_exception_caught_listeners_(false), deoptimized_methods_lock_("deoptimized methods lock"), deoptimization_enabled_(false), @@ -373,6 +374,14 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev dex_pc_listeners_.push_back(listener); have_dex_pc_listeners_ = true; } + if ((events & kFieldRead) != 0) { + field_read_listeners_.push_back(listener); + have_field_read_listeners_ = true; + } + if ((events & kFieldWritten) != 0) { + field_write_listeners_.push_back(listener); + have_field_write_listeners_ = true; + } if ((events & kExceptionCaught) != 0) { exception_caught_listeners_.push_back(listener); have_exception_caught_listeners_ = true; @@ -410,6 +419,22 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t } have_dex_pc_listeners_ = dex_pc_listeners_.size() > 0; } + if ((events & kFieldRead) != 0) { + bool contains = std::find(field_read_listeners_.begin(), field_read_listeners_.end(), + listener) != field_read_listeners_.end(); + if (contains) { + field_read_listeners_.remove(listener); + } + have_field_read_listeners_ = field_read_listeners_.size() > 0; + } + if ((events & kFieldWritten) != 0) { + bool contains = std::find(field_write_listeners_.begin(), field_write_listeners_.end(), + listener) != field_write_listeners_.end(); + if (contains) { + field_write_listeners_.remove(listener); + } + have_field_write_listeners_ = field_write_listeners_.size() > 0; + } if ((events & kExceptionCaught) != 0) { exception_caught_listeners_.remove(listener); have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0; @@ -743,6 +768,30 @@ void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_o } } +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 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 copy(field_write_listeners_); + for (InstrumentationListener* listener : copy) { + listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value); + } + } +} + void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 2a9c35f5a..3de07283b 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -28,6 +28,7 @@ namespace art { namespace mirror { + class ArtField; class ArtMethod; class Class; class Object; @@ -78,6 +79,14 @@ struct InstrumentationListener { mirror::ArtMethod* method, uint32_t new_dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + // Call-back for when we read from a field. + virtual void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, mirror::ArtField* field) = 0; + + // Call-back for when we write into a field. + virtual void FieldWritten(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, mirror::ArtField* field, const JValue& field_value) = 0; + // Call-back when an exception is caught. virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, @@ -92,11 +101,13 @@ struct InstrumentationListener { class Instrumentation { public: enum InstrumentationEvent { - kMethodEntered = 1, - kMethodExited = 2, - kMethodUnwind = 4, - kDexPcMoved = 8, - kExceptionCaught = 16 + kMethodEntered = 1 << 0, + kMethodExited = 1 << 1, + kMethodUnwind = 1 << 2, + kDexPcMoved = 1 << 3, + kFieldRead = 1 << 4, + kFieldWritten = 1 << 5, + kExceptionCaught = 1 << 6, }; Instrumentation(); @@ -217,6 +228,14 @@ class Instrumentation { return have_dex_pc_listeners_; } + bool HasFieldReadListeners() const { + return have_field_read_listeners_; + } + + bool HasFieldWriteListeners() const { + return have_field_write_listeners_; + } + bool IsActive() const { return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ || have_exception_caught_listeners_ || have_method_unwind_listeners_; @@ -256,6 +275,26 @@ class Instrumentation { } } + // Inform listeners that we read a field (only supported by the interpreter). + void FieldReadEvent(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, + mirror::ArtField* field) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(HasFieldReadListeners())) { + FieldReadEventImpl(thread, this_object, method, dex_pc, field); + } + } + + // Inform listeners that we write a field (only supported by the interpreter). + void FieldWriteEvent(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, + mirror::ArtField* field, const JValue& field_value) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(HasFieldWriteListeners())) { + FieldWriteEventImpl(thread, this_object, method, dex_pc, field, field_value); + } + } + // Inform listeners that an exception was caught. void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, @@ -313,6 +352,14 @@ class Instrumentation { void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, uint32_t dex_pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FieldReadEventImpl(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, + mirror::ArtField* field) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FieldWriteEventImpl(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, + mirror::ArtField* field, const JValue& field_value) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code? bool instrumentation_stubs_installed_; @@ -345,6 +392,14 @@ class Instrumentation { // instrumentation_lock_. bool have_dex_pc_listeners_; + // Do we have any listeners for field read events? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_field_read_listeners_; + + // Do we have any listeners for field write events? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_field_write_listeners_; + // Do we have any exception caught listeners? Short-cut to avoid taking the instrumentation_lock_. bool have_exception_caught_listeners_; @@ -353,6 +408,8 @@ class Instrumentation { std::list method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list field_read_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list field_write_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); // The set of methods being deoptimized (by the debugger) which must be executed with interpreter diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index 66ebb96d4..1477324ca 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -197,6 +197,17 @@ struct JdwpState { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /* + * A field of interest has been accessed or modified. This is used for field access and field + * modification events. + * + * "fieldValue" is non-null for field modification events only. + * "is_modification" is true for field modification, false for field access. + */ + bool PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, FieldId fieldId, + ObjectId thisPtr, const JValue* fieldValue, bool is_modification) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + /* * An exception has been thrown. * * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught. diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 9b3ea2e6c..962780290 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -121,26 +121,14 @@ struct ModBasket { /* nothing for StepOnly -- handled differently */ }; -/* - * Dump an event to the log file. - */ -static void dumpEvent(const JdwpEvent* pEvent) { - LOG(INFO) << StringPrintf("Event id=0x%4x %p (prev=%p next=%p):", pEvent->requestId, pEvent, pEvent->prev, pEvent->next); - LOG(INFO) << " kind=" << pEvent->eventKind << " susp=" << pEvent->suspend_policy << " modCount=" << pEvent->modCount; - - for (int i = 0; i < pEvent->modCount; i++) { - const JdwpEventMod* pMod = &pEvent->mods[i]; - LOG(INFO) << " " << pMod->modKind; - /* TODO - show details */ - } -} - static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { switch (eventKind) { case EK_METHOD_ENTRY: case EK_METHOD_EXIT: case EK_METHOD_EXIT_WITH_RETURN_VALUE: case EK_SINGLE_STEP: + case EK_FIELD_ACCESS: + case EK_FIELD_MODIFICATION: return true; default: return false; @@ -177,9 +165,6 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { if (status != ERR_NONE) { return status; } - } else if (pMod->modKind == MK_FIELD_ONLY) { - /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */ - dumpEvent(pEvent); /* TODO - need for field watches */ } } if (NeedsFullDeoptimization(pEvent->eventKind)) { @@ -843,6 +828,86 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in return match_count != 0; } +bool JdwpState::PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, FieldId fieldId, + ObjectId thisPtr, const JValue* fieldValue, bool is_modification) { + ModBasket basket; + basket.pLoc = pLoc; + basket.classId = pLoc->class_id; + basket.thisPtr = thisPtr; + basket.threadId = Dbg::GetThreadSelfId(); + basket.className = Dbg::GetClassName(pLoc->class_id); + basket.field = fieldId; + + if (InvokeInProgress()) { + VLOG(jdwp) << "Not posting field event during invoke"; + return false; + } + + // Get field's reference type tag. + JDWP::JdwpTypeTag type_tag; + uint32_t class_status; // unused here. + JdwpError error = Dbg::GetClassInfo(typeId, &type_tag, &class_status, NULL); + if (error != ERR_NONE) { + return false; + } + + // Get instance type tag. + uint8_t tag; + error = Dbg::GetObjectTag(thisPtr, tag); + if (error != ERR_NONE) { + return false; + } + + int match_count = 0; + ExpandBuf* pReq = NULL; + JdwpSuspendPolicy suspend_policy = SP_NONE; + { + MutexLock mu(Thread::Current(), event_list_lock_); + JdwpEvent** match_list = AllocMatchList(event_list_size_); + + if (is_modification) { + FindMatchingEvents(EK_FIELD_MODIFICATION, &basket, match_list, &match_count); + } else { + FindMatchingEvents(EK_FIELD_ACCESS, &basket, match_list, &match_count); + } + if (match_count != 0) { + VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " + << basket.className << "." << Dbg::GetMethodName(pLoc->method_id) + << StringPrintf(" thread=%#" PRIx64 " dex_pc=%#" PRIx64 ")", + basket.threadId, pLoc->dex_pc); + + suspend_policy = scanSuspendPolicy(match_list, match_count); + VLOG(jdwp) << " suspend_policy=" << suspend_policy; + + pReq = eventPrep(); + expandBufAdd1(pReq, suspend_policy); + expandBufAdd4BE(pReq, match_count); + + for (int i = 0; i < match_count; i++) { + expandBufAdd1(pReq, match_list[i]->eventKind); + expandBufAdd4BE(pReq, match_list[i]->requestId); + expandBufAdd8BE(pReq, basket.threadId); + expandBufAddLocation(pReq, *pLoc); + expandBufAdd1(pReq, type_tag); + expandBufAddRefTypeId(pReq, typeId); + expandBufAddFieldId(pReq, fieldId); + expandBufAdd1(pReq, tag); + expandBufAddObjectId(pReq, thisPtr); + if (is_modification) { + Dbg::OutputFieldValue(fieldId, fieldValue, pReq); + } + } + } + + CleanupMatchList(match_list, match_count); + } + + Dbg::ManageDeoptimization(); + + SendRequestAndPossiblySuspend(pReq, suspend_policy, basket.threadId); + return match_count != 0; +} + /* * A thread is starting or stopping. * diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index c2a2b5415..8f224d224 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -353,8 +353,8 @@ static JdwpError VM_DisposeObjects(JdwpState*, Request& request, ExpandBuf*) static JdwpError VM_Capabilities(JdwpState*, Request&, ExpandBuf* reply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - expandBufAdd1(reply, false); // canWatchFieldModification - expandBufAdd1(reply, false); // canWatchFieldAccess + expandBufAdd1(reply, true); // canWatchFieldModification + expandBufAdd1(reply, true); // canWatchFieldAccess expandBufAdd1(reply, true); // canGetBytecodes expandBufAdd1(reply, true); // canGetSyntheticAttribute expandBufAdd1(reply, true); // canGetOwnedMonitorInfo diff --git a/runtime/trace.cc b/runtime/trace.cc index 1f2447871..b85eb7e6b 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -549,6 +549,21 @@ void Trace::DexPcMoved(Thread* thread, mirror::Object* this_object, LOG(ERROR) << "Unexpected dex PC event in tracing " << PrettyMethod(method) << " " << new_dex_pc; }; +void Trace::FieldRead(Thread* /*thread*/, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // We're not recorded to listen to this kind of event, so complain. + LOG(ERROR) << "Unexpected field read event in tracing " << PrettyMethod(method) << " " << dex_pc; +} + +void Trace::FieldWritten(Thread* /*thread*/, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field, + const JValue& field_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // We're not recorded to listen to this kind of event, so complain. + LOG(ERROR) << "Unexpected field write event in tracing " << PrettyMethod(method) << " " << dex_pc; +} + void Trace::MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, uint32_t dex_pc) { uint32_t thread_clock_diff = 0; diff --git a/runtime/trace.h b/runtime/trace.h index 1af12831e..bf4995a45 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -32,6 +32,7 @@ namespace art { namespace mirror { + class ArtField; class ArtMethod; } // namespace mirror class Thread; @@ -54,7 +55,7 @@ enum TracingMode { kSampleProfilingActive, }; -class Trace : public instrumentation::InstrumentationListener { +class Trace FINAL : public instrumentation::InstrumentationListener { public: enum TraceFlag { kTraceCountAllocs = 1, @@ -78,23 +79,31 @@ class Trace : public instrumentation::InstrumentationListener { void CompareAndUpdateStackTrace(Thread* thread, std::vector* stack_trace) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void MethodEntered(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void MethodExited(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc, - const JValue& return_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t new_dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, - mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, - mirror::Throwable* exception_object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // InstrumentationListener implementation. + void MethodEntered(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; + void MethodExited(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, + const JValue& return_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; + void MethodUnwind(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; + void DexPcMoved(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t new_dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; + void FieldRead(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; + void FieldWritten(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field, + const JValue& field_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; + void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; // Reuse an old stack trace if it exists, otherwise allocate a new one. static std::vector* AllocStackTrace(); -- 2.11.0