summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastien Hertz <shertz@google.com>2014-04-04 17:51:34 +0200
committerSebastien Hertz <shertz@google.com>2014-04-25 09:35:54 +0200
commit479fc1ecc12fa6560ca90d841c4d5174fb346618 (patch)
tree9c89abea89494c90934b260f42f81e8ca83c3611
parent96a4f29350bf279d48bff70e21e3264cce216683 (diff)
downloadart-479fc1ecc12fa6560ca90d841c4d5174fb346618.zip
art-479fc1ecc12fa6560ca90d841c4d5174fb346618.tar.gz
art-479fc1ecc12fa6560ca90d841c4d5174fb346618.tar.bz2
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
-rw-r--r--runtime/interpreter/interpreter_common.h74
-rw-r--r--runtime/jdwp/jdwp_event.cc13
-rw-r--r--runtime/jdwp/jdwp_handler.cc4
-rw-r--r--runtime/mirror/art_field.cc21
-rw-r--r--runtime/mirror/art_field.h4
-rw-r--r--runtime/verifier/method_verifier.cc31
6 files changed, 116 insertions, 31 deletions
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 65bdf0e..cc1fa0c 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<Primitive::Type field_type>
+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<uint8_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimByte:
+ field_value.SetB(static_cast<int8_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimChar:
+ field_value.SetC(static_cast<uint16_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimShort:
+ field_value.SetS(static_cast<int16_t>(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<FindFieldType find_type, Primitive::Type field_type, bool do_access_check, bool transaction_active>
@@ -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<field_type>(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<transaction_active>(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<field_type>(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<transaction_active>(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index adc1074..223b7a1 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 8ef375b..4843c2b 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 f91cab1..7b0b94c 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<ArtField>* 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 0daa838..ba70cc6 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 535c76d..0857fe3 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<mirror::ArtField>* 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<uint32_t>(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,