Update JDWP event filtering to avoid useless ids
To reduce the number of JDWP ids in the debugger, we update the event filtering
support to work with runtime objects (Thread, Class, Object, ...) instead of
JDWP ids (ThreadId, RefTypeId, ObjectId, ...).
We used to create useless JDWP ids for events even if they were not reported
because of event filtering (thread only, class only, instance only, ...). Now
we only create JDWP ids when we know we're going to report an event.
Bug: 17343664
(cherry picked from commit d539167b7f11136fe570a77aff2ee4935842007a)
Change-Id: I8619e219733fc2fa3569f473b7bd8d9af4181f2b
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6c37402..aced954 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -311,7 +311,7 @@
static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
static Dbg::HpsgWhat gDdmNhsgWhat;
-static ObjectRegistry* gRegistry = nullptr;
+ObjectRegistry* Dbg::gRegistry = nullptr;
// Recent allocation tracking.
AllocRecord* Dbg::recent_allocation_records_ = nullptr; // TODO: CircularBuffer<AllocRecord>
@@ -401,7 +401,7 @@
static mirror::Array* DecodeNonNullArray(JDWP::RefTypeId id, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(id, error);
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id, error);
if (o == nullptr) {
*error = JDWP::ERR_INVALID_OBJECT;
return nullptr;
@@ -416,7 +416,7 @@
static mirror::Class* DecodeClass(JDWP::RefTypeId id, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(id, error);
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id, error);
if (o == nullptr) {
*error = JDWP::ERR_INVALID_OBJECT;
return nullptr;
@@ -434,7 +434,7 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* thread_peer = gRegistry->Get<mirror::Object*>(thread_id, error);
+ mirror::Object* thread_peer = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_id, error);
if (thread_peer == nullptr) {
// This isn't even an object.
*error = JDWP::ERR_INVALID_OBJECT;
@@ -511,8 +511,7 @@
*
* Null objects are tagged JT_OBJECT.
*/
-static JDWP::JdwpTag TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::JdwpTag Dbg::TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o) {
return (o == nullptr) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
}
@@ -842,8 +841,13 @@
if (!o->IsClass()) {
return StringPrintf("non-class %p", o); // This is only used for debugging output anyway.
}
+ return GetClassName(o->AsClass());
+}
+
+std::string Dbg::GetClassName(mirror::Class* klass) {
+ DCHECK(klass != nullptr);
std::string temp;
- return DescriptorToName(o->AsClass()->GetDescriptor(&temp));
+ return DescriptorToName(klass->GetDescriptor(&temp));
}
JDWP::JdwpError Dbg::GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId* class_object_id) {
@@ -1108,8 +1112,7 @@
gRegistry->DisposeObject(object_id, reference_count);
}
-static JDWP::JdwpTypeTag GetTypeTag(mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::JdwpTypeTag Dbg::GetTypeTag(mirror::Class* klass) {
DCHECK(klass != nullptr);
if (klass->IsArrayClass()) {
return JDWP::TT_ARRAY;
@@ -1422,17 +1425,7 @@
return JDWP::ERR_NONE;
}
-bool Dbg::MatchType(JDWP::RefTypeId instance_class_id, JDWP::RefTypeId class_id) {
- JDWP::JdwpError error;
- mirror::Class* c1 = DecodeClass(instance_class_id, &error);
- CHECK(c1 != nullptr);
- mirror::Class* c2 = DecodeClass(class_id, &error);
- CHECK(c2 != nullptr);
- return c2->IsAssignableFrom(c1);
-}
-
-static JDWP::FieldId ToFieldId(const mirror::ArtField* f)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::FieldId Dbg::ToFieldId(const mirror::ArtField* f) {
CHECK(!kMovingFields);
return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
}
@@ -1455,10 +1448,49 @@
return reinterpret_cast<mirror::ArtMethod*>(static_cast<uintptr_t>(mid));
}
-static void SetLocation(JDWP::JdwpLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
+bool Dbg::MatchThread(JDWP::ObjectId expected_thread_id, Thread* event_thread) {
+ CHECK(event_thread != nullptr);
+ JDWP::JdwpError error;
+ mirror::Object* expected_thread_peer = gRegistry->Get<mirror::Object*>(expected_thread_id,
+ &error);
+ return expected_thread_peer == event_thread->GetPeer();
+}
+
+bool Dbg::MatchLocation(const JDWP::JdwpLocation& expected_location,
+ const JDWP::EventLocation& event_location) {
+ if (expected_location.dex_pc != event_location.dex_pc) {
+ return false;
+ }
+ mirror::ArtMethod* m = FromMethodId(expected_location.method_id);
+ return m == event_location.method;
+}
+
+bool Dbg::MatchType(mirror::Class* event_class, JDWP::RefTypeId class_id) {
+ JDWP::JdwpError error;
+ mirror::Class* expected_class = DecodeClass(class_id, &error);
+ CHECK(expected_class != nullptr);
+ return expected_class->IsAssignableFrom(event_class);
+}
+
+bool Dbg::MatchField(JDWP::RefTypeId expected_type_id, JDWP::FieldId expected_field_id,
+ mirror::ArtField* event_field) {
+ mirror::ArtField* expected_field = FromFieldId(expected_field_id);
+ if (expected_field != event_field) {
+ return false;
+ }
+ return Dbg::MatchType(event_field->GetDeclaringClass(), expected_type_id);
+}
+
+bool Dbg::MatchInstance(JDWP::ObjectId expected_instance_id, mirror::Object* event_instance) {
+ JDWP::JdwpError error;
+ mirror::Object* modifier_instance = gRegistry->Get<mirror::Object*>(expected_instance_id, &error);
+ return modifier_instance == event_instance;
+}
+
+void Dbg::SetJdwpLocation(JDWP::JdwpLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (m == nullptr) {
- memset(&location, 0, sizeof(location));
+ memset(&location, 0, sizeof(*location));
} else {
mirror::Class* c = m->GetDeclaringClass();
location->type_tag = GetTypeTag(c);
@@ -1757,7 +1789,7 @@
return error;
}
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error);
if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
@@ -1819,7 +1851,7 @@
uint64_t value, int width, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
JDWP::JdwpError error;
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error);
if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
@@ -1853,7 +1885,7 @@
f->Set32<false>(o, value);
}
} else {
- mirror::Object* v = gRegistry->Get<mirror::Object*>(value, &error);
+ mirror::Object* v = Dbg::GetObjectRegistry()->Get<mirror::Object*>(value, &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
@@ -1987,7 +2019,8 @@
static mirror::Object* DecodeThreadGroup(ScopedObjectAccessUnchecked& soa,
JDWP::ObjectId thread_group_id, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, error);
+ mirror::Object* thread_group = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_group_id,
+ error);
if (*error != JDWP::ERR_NONE) {
return nullptr;
}
@@ -2062,8 +2095,9 @@
const int32_t size = size_field->GetInt(groups_array_list);
// Copy the first 'size' elements out of the array into the result.
+ ObjectRegistry* registry = Dbg::GetObjectRegistry();
for (int32_t i = 0; i < size; ++i) {
- child_thread_group_ids->push_back(gRegistry->Add(groups_array->Get(i)));
+ child_thread_group_ids->push_back(registry->Add(groups_array->Get(i)));
}
}
@@ -2296,7 +2330,7 @@
if (depth_ >= start_frame_) {
JDWP::FrameId frame_id(GetFrameId());
JDWP::JdwpLocation location;
- SetLocation(&location, GetMethod(), GetDexPc());
+ SetJdwpLocation(&location, GetMethod(), GetDexPc());
VLOG(jdwp) << StringPrintf(" Frame %3zd: id=%3" PRIu64 " ", depth_, frame_id) << location;
expandBufAdd8BE(buf_, frame_id);
expandBufAddLocation(buf_, location);
@@ -2328,8 +2362,12 @@
}
JDWP::ObjectId Dbg::GetThreadSelfId() {
+ return GetThreadId(Thread::Current());
+}
+
+JDWP::ObjectId Dbg::GetThreadId(Thread* thread) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- return gRegistry->Add(soa.Self()->GetPeer());
+ return gRegistry->Add(thread->GetPeer());
}
void Dbg::SuspendVM() {
@@ -2733,16 +2771,15 @@
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);
+static void SetEventLocation(JDWP::EventLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(location != nullptr);
+ if (m == nullptr) {
+ memset(location, 0, sizeof(*location));
+ } else {
+ location->method = m;
+ location->dex_pc = (m->IsNative() || m->IsProxyMethod()) ? static_cast<uint32_t>(-1) : dex_pc;
}
- return this_id;
}
void Dbg::PostLocationEvent(mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object,
@@ -2752,12 +2789,10 @@
}
DCHECK(m != nullptr);
DCHECK_EQ(m->IsStatic(), this_object == nullptr);
- JDWP::JdwpLocation location;
- SetLocation(&location, m, dex_pc);
+ JDWP::EventLocation location;
+ SetEventLocation(&location, m, dex_pc);
- // We need 'this' for InstanceOnly filters only.
- JDWP::ObjectId this_id = GetThisObjectIdForEvent(this_object);
- gJdwpState->PostLocationEvent(&location, this_id, event_flags, return_value);
+ gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value);
}
void Dbg::PostFieldAccessEvent(mirror::ArtMethod* m, int dex_pc,
@@ -2767,14 +2802,10 @@
}
DCHECK(m != nullptr);
DCHECK(f != nullptr);
- JDWP::JdwpLocation location;
- SetLocation(&location, m, dex_pc);
+ JDWP::EventLocation location;
+ SetEventLocation(&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);
+ gJdwpState->PostFieldEvent(&location, f, this_object, nullptr, false);
}
void Dbg::PostFieldModificationEvent(mirror::ArtMethod* m, int dex_pc,
@@ -2786,14 +2817,10 @@
DCHECK(m != nullptr);
DCHECK(f != nullptr);
DCHECK(field_value != nullptr);
- JDWP::JdwpLocation location;
- SetLocation(&location, m, dex_pc);
+ JDWP::EventLocation location;
+ SetEventLocation(&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);
+ gJdwpState->PostFieldEvent(&location, f, this_object, field_value, true);
}
void Dbg::PostException(const ThrowLocation& throw_location,
@@ -2802,33 +2829,20 @@
if (!IsDebuggerActive()) {
return;
}
+ JDWP::EventLocation exception_throw_location;
+ SetEventLocation(&exception_throw_location, throw_location.GetMethod(), throw_location.GetDexPc());
+ JDWP::EventLocation exception_catch_location;
+ SetEventLocation(&exception_catch_location, catch_method, catch_dex_pc);
- JDWP::JdwpLocation jdwp_throw_location;
- SetLocation(&jdwp_throw_location, throw_location.GetMethod(), throw_location.GetDexPc());
- JDWP::JdwpLocation catch_location;
- SetLocation(&catch_location, catch_method, catch_dex_pc);
-
- // 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());
-
- gJdwpState->PostException(&jdwp_throw_location, exception_id, exception_class_id, &catch_location,
- this_id);
+ gJdwpState->PostException(&exception_throw_location, exception_object, &exception_catch_location,
+ throw_location.GetThis());
}
void Dbg::PostClassPrepare(mirror::Class* c) {
if (!IsDebuggerActive()) {
return;
}
-
- // OLD-TODO - we currently always send both "verified" and "prepared" since
- // debuggers seem to like that. There might be some advantage to honesty,
- // since the class may not yet be verified.
- int state = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
- JDWP::JdwpTypeTag tag = GetTypeTag(c);
- std::string temp;
- gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), c->GetDescriptor(&temp), state);
+ gJdwpState->PostClassPrepare(c);
}
void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object,
@@ -3245,7 +3259,7 @@
self_suspend_ = true;
} else {
soa.Self()->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
- jobject thread_peer = gRegistry->GetJObject(thread_id);
+ jobject thread_peer = Dbg::GetObjectRegistry()->GetJObject(thread_id);
bool timed_out;
Thread* suspended_thread;
{
@@ -3908,9 +3922,7 @@
void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
if (IsDebuggerActive()) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
- gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
+ gJdwpState->PostThreadChange(t, type == CHUNK_TYPE("THCR"));
}
Dbg::DdmSendThreadNotification(t, type);
}