From 479fc1ecc12fa6560ca90d841c4d5174fb346618 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 4 Apr 2014 17:51:34 +0200 Subject: [PATCH] Support field watchpoint in interpreter We report field read/write events to instrumentation from the interpreter. This allows it to send JDWP field access and field modification events to debugger. This completes CL https://android-review.googlesource.com/90390. We also fix the JDWP FieldOnly modifier by introducing ModBasket.fieldTypeID. We incorrectly used ModBasket.classId which is actually dedicated to ClassOnly modifier based on thread's location's class id. Finally, we now enable canWatchFieldModification and canWatchFieldAccess JDWP capabilities so a debugger can request these events to be reported. Bug: 8267708 Change-Id: I987852ad47abb27b2f7e78544a8189c7a4e2f462 --- runtime/interpreter/interpreter_common.h | 74 +++++++++++++++++++++++++++++++- runtime/jdwp/jdwp_event.cc | 13 ++++-- runtime/jdwp/jdwp_handler.cc | 4 +- runtime/mirror/art_field.cc | 21 +++++++++ runtime/mirror/art_field.h | 4 ++ runtime/verifier/method_verifier.cc | 31 +++---------- 6 files changed, 116 insertions(+), 31 deletions(-) diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 65bdf0e0a..cc1fa0c94 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -169,6 +169,13 @@ static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, return false; } } + // Report this field access to instrumentation if needed. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldReadListeners())) { + Object* this_object = f->IsStatic() ? nullptr : obj; + instrumentation->FieldReadEvent(self, this_object, shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), f); + } uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimBoolean: @@ -210,6 +217,17 @@ static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* ins return false; } MemberOffset field_offset(inst->VRegC_22c()); + // Report this field access to instrumentation if needed. Since we only have the offset of + // the field from the base of the object, we need to look for it first. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldReadListeners())) { + ArtField* f = ArtField::FindInstanceFieldWithOffset(obj->GetClass(), + field_offset.Uint32Value()); + DCHECK(f != nullptr); + DCHECK(!f->IsStatic()); + instrumentation->FieldReadEvent(Thread::Current(), obj, shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), f); + } const bool is_volatile = false; // iget-x-quick only on non volatile fields. const uint32_t vregA = inst->VRegA_22c(inst_data); switch (field_type) { @@ -228,6 +246,39 @@ static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* ins return true; } +template +static inline JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + JValue field_value; + switch (field_type) { + case Primitive::kPrimBoolean: + field_value.SetZ(static_cast(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimByte: + field_value.SetB(static_cast(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimChar: + field_value.SetC(static_cast(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimShort: + field_value.SetS(static_cast(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimInt: + field_value.SetI(shadow_frame.GetVReg(vreg)); + break; + case Primitive::kPrimLong: + field_value.SetJ(shadow_frame.GetVRegLong(vreg)); + break; + case Primitive::kPrimNot: + field_value.SetL(shadow_frame.GetVRegReference(vreg)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + break; + } + return field_value; +} + // Handles iput-XXX and sput-XXX instructions. // Returns true on success, otherwise throws an exception and returns false. template @@ -254,6 +305,15 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, } } uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); + // Report this field access to instrumentation if needed. Since we only have the offset of + // the field from the base of the object, we need to look for it first. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { + JValue field_value = GetFieldValue(shadow_frame, vregA); + Object* this_object = f->IsStatic() ? nullptr : obj; + instrumentation->FieldWriteEvent(self, this_object, shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), f, field_value); + } switch (field_type) { case Primitive::kPrimBoolean: f->SetBoolean(obj, shadow_frame.GetVReg(vregA)); @@ -309,8 +369,20 @@ static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instructio return false; } MemberOffset field_offset(inst->VRegC_22c()); - const bool is_volatile = false; // iput-x-quick only on non volatile fields. const uint32_t vregA = inst->VRegA_22c(inst_data); + // Report this field modification to instrumentation if needed. Since we only have the offset of + // the field from the base of the object, we need to look for it first. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { + ArtField* f = ArtField::FindInstanceFieldWithOffset(obj->GetClass(), + field_offset.Uint32Value()); + DCHECK(f != nullptr); + DCHECK(!f->IsStatic()); + JValue field_value = GetFieldValue(shadow_frame, vregA); + instrumentation->FieldWriteEvent(Thread::Current(), obj, shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), f, field_value); + } + const bool is_volatile = false; // iput-x-quick only on non volatile fields. switch (field_type) { case Primitive::kPrimInt: obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index adc1074d8..223b7a112 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -108,7 +108,7 @@ namespace JDWP { */ struct ModBasket { ModBasket() : pLoc(NULL), threadId(0), classId(0), excepClassId(0), - caught(false), field(0), thisPtr(0) { } + caught(false), fieldTypeID(0), fieldId(0), thisPtr(0) { } const JdwpLocation* pLoc; /* LocationOnly */ std::string className; /* ClassMatch/ClassExclude */ @@ -116,7 +116,8 @@ struct ModBasket { RefTypeId classId; /* ClassOnly */ RefTypeId excepClassId; /* ExceptionOnly */ bool caught; /* ExceptionOnly */ - FieldId field; /* FieldOnly */ + RefTypeId fieldTypeID; /* FieldOnly */ + FieldId fieldId; /* FieldOnly */ ObjectId thisPtr; /* InstanceOnly */ /* nothing for StepOnly -- handled differently */ }; @@ -457,7 +458,10 @@ static bool ModsMatch(JdwpEvent* pEvent, ModBasket* basket) } break; case MK_FIELD_ONLY: - if (!Dbg::MatchType(basket->classId, pMod->fieldOnly.refTypeId) || pMod->fieldOnly.fieldId != basket->field) { + if (pMod->fieldOnly.fieldId != basket->fieldId) { + return false; + } + if (!Dbg::MatchType(basket->fieldTypeID, pMod->fieldOnly.refTypeId)) { return false; } break; @@ -848,7 +852,8 @@ bool JdwpState::PostFieldEvent(const JdwpLocation* pLoc, RefTypeId typeId, Field basket.thisPtr = thisPtr; basket.threadId = Dbg::GetThreadSelfId(); basket.className = Dbg::GetClassName(pLoc->class_id); - basket.field = fieldId; + basket.fieldTypeID = typeId; + basket.fieldId = fieldId; if (InvokeInProgress()) { VLOG(jdwp) << "Not posting field event during invoke"; diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 8ef375b78..4843c2b5e 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -354,8 +354,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/mirror/art_field.cc b/runtime/mirror/art_field.cc index f91cab103..7b0b94cd7 100644 --- a/runtime/mirror/art_field.cc +++ b/runtime/mirror/art_field.cc @@ -19,6 +19,7 @@ #include "art_field-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" +#include "object_array-inl.h" #include "object_utils.h" #include "runtime.h" #include "scoped_thread_state_change.h" @@ -69,5 +70,25 @@ void ArtField::VisitRoots(RootCallback* callback, void* arg) { } } +// TODO: we could speed up the search if fields are ordered by offsets. +ArtField* ArtField::FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) { + DCHECK(klass != nullptr); + ObjectArray* instance_fields = klass->GetIFields(); + if (instance_fields != nullptr) { + for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) { + mirror::ArtField* field = instance_fields->GetWithoutChecks(i); + if (field->GetOffset().Uint32Value() == field_offset) { + return field; + } + } + } + // We did not find field in the class: look into superclass. + if (klass->GetSuperClass() != NULL) { + return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset); + } else { + return nullptr; + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h index 0daa838a2..ba70cc64e 100644 --- a/runtime/mirror/art_field.h +++ b/runtime/mirror/art_field.h @@ -132,6 +132,10 @@ class MANAGED ArtField : public Object { return (GetAccessFlags() & kAccVolatile) != 0; } + // Returns an instance field with this offset in the given class or nullptr if not found. + static ArtField* FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 535c76dd1..0857fe33b 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3596,29 +3596,6 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty } } -// Look for an instance field with this offset. -// TODO: we may speed up the search if offsets are sorted by doing a quick search. -static mirror::ArtField* FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ObjectArray* instance_fields = klass->GetIFields(); - if (instance_fields != NULL) { - for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) { - mirror::ArtField* field = instance_fields->Get(i); - if (field->GetOffset().Uint32Value() == field_offset) { - return field; - } - } - } - // We did not find field in class: look into superclass. - if (klass->GetSuperClass() != NULL) { - return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset); - } else { - VLOG(verifier) << "Failed to find instance field at offset '" << field_offset - << "' from '" << PrettyDescriptor(klass) << "'"; - return nullptr; - } -} - mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) { DCHECK(inst->Opcode() == Instruction::IGET_QUICK || @@ -3633,7 +3610,13 @@ mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, return nullptr; } uint32_t field_offset = static_cast(inst->VRegC_22c()); - return FindInstanceFieldWithOffset(object_type.GetClass(), field_offset); + mirror::ArtField* f = mirror::ArtField::FindInstanceFieldWithOffset(object_type.GetClass(), + field_offset); + if (f == nullptr) { + VLOG(verifier) << "Failed to find instance field at offset '" << field_offset + << "' from '" << PrettyDescriptor(object_type.GetClass()) << "'"; + } + return f; } void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, -- 2.11.0