summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2017-06-16 08:56:43 -0700
committer Alex Light <allight@google.com> 2017-06-16 11:22:26 -0700
commite00ec30588a36eed90aa24eaca9dbc7520f17bf7 (patch)
treec31d512d62ae2d33b3f57a6549b4c585e0235627
parent61944b0c756309a6cf4a3f03fe2aa515e3721621 (diff)
Make native field operations call instrumentation listeners.
Previously native reads and writes of fields would not be reported to instrumentation listeners. We filter these events out from the debugger since currently the debugger will deadlock if it tries to propagate them through JDWP. Bug: 62712031 Test: ./test.py --host --trace -j40 Test: ./art/tools/run-jdwp-tests.sh --mode=host Test: Manual Change-Id: Ibc75248bdca06537d8b4ff7bb890546136ffa161
-rw-r--r--runtime/debugger.cc6
-rw-r--r--runtime/jni_internal.cc97
-rw-r--r--runtime/jvalue-inl.h18
-rw-r--r--runtime/jvalue.h2
4 files changed, 116 insertions, 7 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7e70b7564c..12bdb32fec 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2927,7 +2927,8 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec
void Dbg::PostFieldAccessEvent(ArtMethod* m, int dex_pc,
mirror::Object* this_object, ArtField* f) {
- if (!IsDebuggerActive()) {
+ // TODO We should send events for native methods.
+ if (!IsDebuggerActive() || m->IsNative()) {
return;
}
DCHECK(m != nullptr);
@@ -2941,7 +2942,8 @@ void Dbg::PostFieldAccessEvent(ArtMethod* m, int dex_pc,
void Dbg::PostFieldModificationEvent(ArtMethod* m, int dex_pc,
mirror::Object* this_object, ArtField* f,
const JValue* field_value) {
- if (!IsDebuggerActive()) {
+ // TODO We should send events for native methods.
+ if (!IsDebuggerActive() || m->IsNative()) {
return;
}
DCHECK(m != nullptr);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 6be0953727..0aabbea2c0 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -40,6 +40,7 @@
#include "interpreter/interpreter.h"
#include "jni_env_ext.h"
#include "java_vm_ext.h"
+#include "jvalue-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/field-inl.h"
@@ -64,6 +65,84 @@ namespace art {
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
+// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
+// up handles for the rare case where these actually do something. Once these functions return it is
+// possible there will be a pending exception if the instrumentation happens to throw one.
+static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_EQ(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+ Thread* self = Thread::Current();
+ ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
+ /*check_suspended*/ true,
+ /*abort_on_error*/ false);
+
+ if (cur_method == nullptr) {
+ // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all
+ // of these changes.
+ return;
+ }
+ DCHECK(cur_method->IsNative());
+ JValue val;
+ val.SetL(self->DecodeJObject(jval));
+ instrumentation->FieldWriteEvent(self,
+ self->DecodeJObject(obj).Ptr(),
+ cur_method,
+ 0, // dex_pc is always 0 since this is a native method.
+ field,
+ val);
+ }
+}
+
+static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+ Thread* self = Thread::Current();
+ ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
+ /*check_suspended*/ true,
+ /*abort_on_error*/ false);
+
+ if (cur_method == nullptr) {
+ // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all
+ // of these changes.
+ return;
+ }
+ DCHECK(cur_method->IsNative());
+ instrumentation->FieldWriteEvent(self,
+ self->DecodeJObject(obj).Ptr(),
+ cur_method,
+ 0, // dex_pc is always 0 since this is a native method.
+ field,
+ val);
+ }
+}
+
+static void NotifyGetField(ArtField* field, jobject obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+ Thread* self = Thread::Current();
+ ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
+ /*check_suspended*/ true,
+ /*abort_on_error*/ false);
+
+ if (cur_method == nullptr) {
+ // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all
+ // of these changes.
+ return;
+ }
+ DCHECK(cur_method->IsNative());
+ instrumentation->FieldReadEvent(self,
+ self->DecodeJObject(obj).Ptr(),
+ cur_method,
+ 0, // dex_pc is always 0 since this is a native method.
+ field);
+ }
+}
+
// Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
// separated with slashes but aren't wrapped with "L;" like regular descriptors
// (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an
@@ -1235,8 +1314,9 @@ class JNI {
CHECK_NON_NULL_ARGUMENT(obj);
CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
- ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
ArtField* f = jni::DecodeArtField(fid);
+ NotifyGetField(f, obj);
+ ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
return soa.AddLocalReference<jobject>(f->GetObject(o));
}
@@ -1244,6 +1324,7 @@ class JNI {
CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
ArtField* f = jni::DecodeArtField(fid);
+ NotifyGetField(f, nullptr);
return soa.AddLocalReference<jobject>(f->GetObject(f->GetDeclaringClass()));
}
@@ -1251,17 +1332,19 @@ class JNI {
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_object);
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid);
ScopedObjectAccess soa(env);
+ ArtField* f = jni::DecodeArtField(fid);
+ NotifySetObjectField(f, java_object, java_value);
ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
- ArtField* f = jni::DecodeArtField(fid);
f->SetObject<false>(o, v);
}
static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid);
ScopedObjectAccess soa(env);
- ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
ArtField* f = jni::DecodeArtField(fid);
+ NotifySetObjectField(f, nullptr, java_value);
+ ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
f->SetObject<false>(f->GetDeclaringClass(), v);
}
@@ -1269,28 +1352,32 @@ class JNI {
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(instance); \
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \
ScopedObjectAccess soa(env); \
- ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
ArtField* f = jni::DecodeArtField(fid); \
+ NotifyGetField(f, instance); \
+ ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
return f->Get ##fn (o)
#define GET_STATIC_PRIMITIVE_FIELD(fn) \
CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \
ScopedObjectAccess soa(env); \
ArtField* f = jni::DecodeArtField(fid); \
+ NotifyGetField(f, nullptr); \
return f->Get ##fn (f->GetDeclaringClass())
#define SET_PRIMITIVE_FIELD(fn, instance, value) \
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(instance); \
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \
ScopedObjectAccess soa(env); \
- ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
ArtField* f = jni::DecodeArtField(fid); \
+ NotifySetPrimitiveField(f, instance, JValue::FromPrimitive<decltype(value)>(value)); \
+ ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
f->Set ##fn <false>(o, value)
#define SET_STATIC_PRIMITIVE_FIELD(fn, value) \
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \
ScopedObjectAccess soa(env); \
ArtField* f = jni::DecodeArtField(fid); \
+ NotifySetPrimitiveField(f, nullptr, JValue::FromPrimitive<decltype(value)>(value)); \
f->Set ##fn <false>(f->GetDeclaringClass(), value)
static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) {
diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h
index b33686c6c5..25e34b2a74 100644
--- a/runtime/jvalue-inl.h
+++ b/runtime/jvalue-inl.h
@@ -27,6 +27,24 @@ inline void JValue::SetL(ObjPtr<mirror::Object> new_l) {
l = new_l.Ptr();
}
+#define DEFINE_FROM(type, chr) \
+ template <> inline JValue JValue::FromPrimitive(type v) { \
+ JValue res; \
+ res.Set ## chr(v); \
+ return res; \
+ }
+
+DEFINE_FROM(uint8_t, Z);
+DEFINE_FROM(int8_t, B);
+DEFINE_FROM(uint16_t, C);
+DEFINE_FROM(int16_t, S);
+DEFINE_FROM(int32_t, I);
+DEFINE_FROM(int64_t, J);
+DEFINE_FROM(float, F);
+DEFINE_FROM(double, D);
+
+#undef DEFINE_FROM
+
} // namespace art
#endif // ART_RUNTIME_JVALUE_INL_H_
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index f61a07c0c0..266abcf399 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -33,6 +33,8 @@ union PACKED(alignof(mirror::Object*)) JValue {
// We default initialize JValue instances to all-zeros.
JValue() : j(0) {}
+ template<typename T> static JValue FromPrimitive(T v);
+
int8_t GetB() const { return b; }
void SetB(int8_t new_b) {
j = ((static_cast<int64_t>(new_b) << 56) >> 56); // Sign-extend to 64 bits.