summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/debugger.cc146
-rw-r--r--runtime/debugger.h17
-rw-r--r--runtime/instrumentation.cc49
-rw-r--r--runtime/instrumentation.h67
-rw-r--r--runtime/jdwp/jdwp.h11
-rw-r--r--runtime/jdwp/jdwp_event.cc99
-rw-r--r--runtime/jdwp/jdwp_handler.cc4
-rw-r--r--runtime/trace.cc15
-rw-r--r--runtime/trace.h45
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();