diff options
author | Sebastien Hertz <shertz@google.com> | 2014-04-15 06:43:14 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-04-15 06:43:14 +0000 |
commit | c311d0c85e17773042daaa7a4abc58b3e3c0a0c1 (patch) | |
tree | 12103a18b43dd863ab7f3fd51ce9600ef4754d95 | |
parent | 0a01b38244053e72f33339e733b3ae5e15a5966b (diff) | |
parent | 3f52eafe5577b8489f90dc8ed5981b3455206147 (diff) | |
download | art-c311d0c85e17773042daaa7a4abc58b3e3c0a0c1.zip art-c311d0c85e17773042daaa7a4abc58b3e3c0a0c1.tar.gz art-c311d0c85e17773042daaa7a4abc58b3e3c0a0c1.tar.bz2 |
Merge "Prepare field watchpoint support"
-rw-r--r-- | runtime/debugger.cc | 146 | ||||
-rw-r--r-- | runtime/debugger.h | 17 | ||||
-rw-r--r-- | runtime/instrumentation.cc | 49 | ||||
-rw-r--r-- | runtime/instrumentation.h | 67 | ||||
-rw-r--r-- | runtime/jdwp/jdwp.h | 11 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_event.cc | 99 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_handler.cc | 4 | ||||
-rw-r--r-- | runtime/trace.cc | 15 | ||||
-rw-r--r-- | runtime/trace.h | 45 |
9 files changed, 371 insertions, 82 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc index bcecbc2..c52a588 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. @@ -625,6 +639,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. @@ -651,11 +673,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(); @@ -686,11 +704,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; } @@ -1590,6 +1604,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<uint8_t>& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -2462,21 +2483,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()) { @@ -2488,8 +2558,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 c1c1dd4..b3e94c3 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; @@ -303,6 +304,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<uint8_t>& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -417,8 +421,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) @@ -544,6 +554,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 525e2b3..bcde9e5 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<InstrumentationListener*> copy(field_read_listeners_); + for (InstrumentationListener* listener : copy) { + listener->FieldRead(thread, this_object, method, dex_pc, field); + } + } +} + +void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object, + mirror::ArtMethod* method, uint32_t dex_pc, + mirror::ArtField* field, const JValue& field_value) const { + if (have_field_write_listeners_) { + // TODO: same comment than DexPcMovedEventImpl. + std::list<InstrumentationListener*> copy(field_write_listeners_); + for (InstrumentationListener* listener : copy) { + listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value); + } + } +} + 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 2a9c35f..3de0728 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<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> field_read_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> field_write_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> 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 66ebb96..1477324 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 9b3ea2e..9627802 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 c2a2b54..8f224d2 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 1f24478..b85eb7e 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 1af1283..bf4995a 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<mirror::ArtMethod*>* 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<mirror::ArtMethod*>* AllocStackTrace(); |