diff options
| -rw-r--r-- | runtime/common_dex_operations.h | 21 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_common.cc | 25 | ||||
| -rw-r--r-- | runtime/method_handles.cc | 2 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 24 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/art_jvmti.h | 18 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/events-inl.h | 66 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/events.cc | 110 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_field.cc | 64 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_field.h | 5 |
9 files changed, 289 insertions, 46 deletions
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 133ddb0721..528db96dd5 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -62,7 +62,7 @@ inline void PerformCall(Thread* self, } template<Primitive::Type field_type> -static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, +static ALWAYS_INLINE bool DoFieldGetCommon(Thread* self, const ShadowFrame& shadow_frame, ObjPtr<mirror::Object> obj, ArtField* field, @@ -85,6 +85,9 @@ static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), field); + if (UNLIKELY(self->IsExceptionPending())) { + return false; + } } switch (field_type) { @@ -113,6 +116,7 @@ static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, LOG(FATAL) << "Unreachable " << field_type; break; } + return true; } template<Primitive::Type field_type, bool do_assignability_check, bool transaction_active> @@ -120,7 +124,7 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, const ShadowFrame& shadow_frame, ObjPtr<mirror::Object> obj, ArtField* field, - const JValue& value) + JValue& value) REQUIRES_SHARED(Locks::mutator_lock_) { field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); @@ -128,15 +132,22 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, // 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())) { - StackHandleScope<1> hs(self); - // Wrap in handle wrapper in case the listener does thread suspension. + StackHandleScope<2> hs(self); + // Save this and return value (if needed) in case the instrumentation causes a suspend. HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); ObjPtr<mirror::Object> this_object = field->IsStatic() ? nullptr : obj; - instrumentation->FieldWriteEvent(self, this_object.Ptr(), + mirror::Object* fake_root = nullptr; + HandleWrapper<mirror::Object> ret(hs.NewHandleWrapper<mirror::Object>( + field_type == Primitive::kPrimNot ? value.GetGCRoot() : &fake_root)); + instrumentation->FieldWriteEvent(self, + this_object.Ptr(), shadow_frame.GetMethod(), shadow_frame.GetDexPC(), field, value); + if (UNLIKELY(self->IsExceptionPending())) { + return false; + } } switch (field_type) { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index d06ac23d3c..1b36c3f12b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -66,7 +66,11 @@ bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst } JValue result; - DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result); + if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result))) { + // Instrumentation threw an error! + CHECK(self->IsExceptionPending()); + return false; + } uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimBoolean: @@ -149,14 +153,18 @@ bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t in field_offset.Uint32Value()); DCHECK(f != nullptr); DCHECK(!f->IsStatic()); - StackHandleScope<1> hs(Thread::Current()); + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); // Save obj in case the instrumentation event has thread suspension. HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&obj); - instrumentation->FieldReadEvent(Thread::Current(), + instrumentation->FieldReadEvent(self, obj.Ptr(), shadow_frame.GetMethod(), shadow_frame.GetDexPC(), f); + if (UNLIKELY(self->IsExceptionPending())) { + return false; + } } // Note: iget-x-quick instructions are only for non-volatile fields. const uint32_t vregA = inst->VRegA_22c(inst_data); @@ -322,15 +330,22 @@ bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint1 DCHECK(f != nullptr); DCHECK(!f->IsStatic()); JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA); - StackHandleScope<1> hs(Thread::Current()); + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); // Save obj in case the instrumentation event has thread suspension. HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&obj); - instrumentation->FieldWriteEvent(Thread::Current(), + mirror::Object* fake_root = nullptr; + HandleWrapper<mirror::Object> ret(hs.NewHandleWrapper<mirror::Object>( + field_type == Primitive::kPrimNot ? field_value.GetGCRoot() : &fake_root)); + instrumentation->FieldWriteEvent(self, obj.Ptr(), shadow_frame.GetMethod(), shadow_frame.GetDexPC(), f, field_value); + if (UNLIKELY(self->IsExceptionPending())) { + return false; + } } // Note: iput-x-quick instructions are only for non-volatile fields. switch (field_type) { diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 090bac1173..f0d3cae4b4 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -822,7 +822,7 @@ inline bool DoFieldPutForInvokePolymorphic(Thread* self, ObjPtr<mirror::Object>& obj, ArtField* field, Primitive::Type field_type, - const JValue& value) + JValue& value) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!Runtime::Current()->IsActiveTransaction()); static const bool kTransaction = false; // Not in a transaction. diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 3ec5b323c8..8daf6f74b3 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -635,36 +635,28 @@ class JvmtiFunctions { return ERR(NOT_IMPLEMENTED); } - static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, - jclass klass ATTRIBUTE_UNUSED, - jfieldID field ATTRIBUTE_UNUSED) { + static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_access_events); - return ERR(NOT_IMPLEMENTED); + return FieldUtil::SetFieldAccessWatch(env, klass, field); } - static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, - jclass klass ATTRIBUTE_UNUSED, - jfieldID field ATTRIBUTE_UNUSED) { + static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_access_events); - return ERR(NOT_IMPLEMENTED); + return FieldUtil::ClearFieldAccessWatch(env, klass, field); } - static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, - jclass klass ATTRIBUTE_UNUSED, - jfieldID field ATTRIBUTE_UNUSED) { + static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_modification_events); - return ERR(NOT_IMPLEMENTED); + return FieldUtil::SetFieldModificationWatch(env, klass, field); } - static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, - jclass klass ATTRIBUTE_UNUSED, - jfieldID field ATTRIBUTE_UNUSED) { + static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_generate_field_modification_events); - return ERR(NOT_IMPLEMENTED); + return FieldUtil::ClearFieldModificationWatch(env, klass, field); } static jvmtiError GetLoadedClasses(jvmtiEnv* env, jint* class_count_ptr, jclass** classes_ptr) { diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 369b2d7c00..8ba3527564 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -34,6 +34,7 @@ #include <memory> #include <type_traits> +#include <unordered_set> #include <jni.h> @@ -46,6 +47,10 @@ #include "jni_env_ext.h" #include "jvmti.h" +namespace art { +class ArtField; +} + namespace openjdkjvmti { class ObjectTagTable; @@ -62,6 +67,15 @@ struct ArtJvmTiEnv : public jvmtiEnv { // Tagging is specific to the jvmtiEnv. std::unique_ptr<ObjectTagTable> object_tag_table; + // Set of watched fields is unique to each jvmtiEnv. + // TODO It might be good to follow the RI and only let one jvmtiEnv ever have the watch caps so + // we can record this on the field directly. We could do this either using free access-flag bits + // or by putting a list in the ClassExt of a field's DeclaringClass. + // TODO Maybe just have an extension to let one put a watch on every field, that would probably be + // good enough maybe since you probably want either a few or all/almost all of them. + std::unordered_set<art::ArtField*> access_watched_fields; + std::unordered_set<art::ArtField*> modify_watched_fields; + ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler); static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) { @@ -194,8 +208,8 @@ static inline JvmtiUniquePtr<char[]> CopyString(jvmtiEnv* env, const char* src, const jvmtiCapabilities kPotentialCapabilities = { .can_tag_objects = 1, - .can_generate_field_modification_events = 0, - .can_generate_field_access_events = 0, + .can_generate_field_modification_events = 1, + .can_generate_field_access_events = 1, .can_get_bytecodes = 0, .can_get_synthetic_attribute = 1, .can_get_owned_monitor_info = 0, diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index cb7e6a9ad0..af99233f90 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -20,6 +20,7 @@ #include <array> #include "events.h" +#include "jni_internal.h" #include "ScopedLocalRef.h" #include "art_jvmti.h" @@ -216,6 +217,71 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A } } +// Need to give custom specializations for FieldAccess and FieldModification since they need to +// filter out which particular fields agents want to get notified on. +// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This +// could make the system more performant. +template <> +inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldModification>(art::Thread* thread, + JNIEnv* jnienv, + jthread jni_thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char type_char, + jvalue val) const { + for (ArtJvmTiEnv* env : envs) { + if (env != nullptr && + ShouldDispatch<ArtJvmtiEvent::kFieldModification>(env, thread) && + env->modify_watched_fields.find( + art::jni::DecodeArtField(field)) != env->modify_watched_fields.end()) { + ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); + jnienv->ExceptionClear(); + auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldModification>(env); + (*callback)(env, + jnienv, + jni_thread, + method, + location, + field_klass, + object, + field, + type_char, + val); + if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { + jnienv->Throw(thr.get()); + } + } + } +} + +template <> +inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFieldAccess>(art::Thread* thread, + JNIEnv* jnienv, + jthread jni_thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field) const { + for (ArtJvmTiEnv* env : envs) { + if (env != nullptr && + ShouldDispatch<ArtJvmtiEvent::kFieldAccess>(env, thread) && + env->access_watched_fields.find( + art::jni::DecodeArtField(field)) != env->access_watched_fields.end()) { + ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred()); + jnienv->ExceptionClear(); + auto callback = impl::GetCallback<ArtJvmtiEvent::kFieldAccess>(env); + (*callback)(env, jnienv, jni_thread, method, location, field_klass, object, field); + if (thr.get() != nullptr && !jnienv->ExceptionCheck()) { + jnienv->Throw(thr.get()); + } + } + } +} + // Need to give a custom specialization for NativeMethodBind since it has to deal with an out // variable. template <> diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index 90bc122220..989b9af591 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -33,6 +33,7 @@ #include "art_jvmti.h" #include "art_method-inl.h" +#include "art_field-inl.h" #include "base/logging.h" #include "gc/allocation_listener.h" #include "gc/gc_pause_listener.h" @@ -433,24 +434,92 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat } // Call-back for when we read from a field. - void FieldRead(art::Thread* self ATTRIBUTE_UNUSED, - art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED, - art::ArtMethod* method ATTRIBUTE_UNUSED, - uint32_t dex_pc ATTRIBUTE_UNUSED, - art::ArtField* field ATTRIBUTE_UNUSED) + void FieldRead(art::Thread* self, + art::Handle<art::mirror::Object> this_object, + art::ArtMethod* method, + uint32_t dex_pc, + art::ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { - return; + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFieldAccess)) { + art::JNIEnvExt* jnienv = self->GetJniEnv(); + // DCHECK(!self->IsExceptionPending()); + ScopedLocalRef<jobject> this_ref(jnienv, AddLocalRef<jobject>(jnienv, this_object.Get())); + ScopedLocalRef<jobject> fklass(jnienv, + AddLocalRef<jobject>(jnienv, + field->GetDeclaringClass().Ptr())); + RunEventCallback<ArtJvmtiEvent::kFieldAccess>(self, + jnienv, + art::jni::EncodeArtMethod(method), + static_cast<jlocation>(dex_pc), + static_cast<jclass>(fklass.get()), + this_ref.get(), + art::jni::EncodeArtField(field)); + } + } + + void FieldWritten(art::Thread* self, + art::Handle<art::mirror::Object> this_object, + art::ArtMethod* method, + uint32_t dex_pc, + art::ArtField* field, + art::Handle<art::mirror::Object> new_val) + REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFieldModification)) { + art::JNIEnvExt* jnienv = self->GetJniEnv(); + // DCHECK(!self->IsExceptionPending()); + ScopedLocalRef<jobject> this_ref(jnienv, AddLocalRef<jobject>(jnienv, this_object.Get())); + ScopedLocalRef<jobject> fklass(jnienv, + AddLocalRef<jobject>(jnienv, + field->GetDeclaringClass().Ptr())); + ScopedLocalRef<jobject> fval(jnienv, AddLocalRef<jobject>(jnienv, new_val.Get())); + jvalue val; + val.l = fval.get(); + RunEventCallback<ArtJvmtiEvent::kFieldModification>( + self, + jnienv, + art::jni::EncodeArtMethod(method), + static_cast<jlocation>(dex_pc), + static_cast<jclass>(fklass.get()), + field->IsStatic() ? nullptr : this_ref.get(), + art::jni::EncodeArtField(field), + 'L', // type_char + val); + } } // Call-back for when we write into a field. - void FieldWritten(art::Thread* self ATTRIBUTE_UNUSED, - art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED, - art::ArtMethod* method ATTRIBUTE_UNUSED, - uint32_t dex_pc ATTRIBUTE_UNUSED, - art::ArtField* field ATTRIBUTE_UNUSED, - const art::JValue& field_value ATTRIBUTE_UNUSED) + void FieldWritten(art::Thread* self, + art::Handle<art::mirror::Object> this_object, + art::ArtMethod* method, + uint32_t dex_pc, + art::ArtField* field, + const art::JValue& field_value) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { - return; + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFieldModification)) { + art::JNIEnvExt* jnienv = self->GetJniEnv(); + DCHECK(!self->IsExceptionPending()); + ScopedLocalRef<jobject> this_ref(jnienv, AddLocalRef<jobject>(jnienv, this_object.Get())); + ScopedLocalRef<jobject> fklass(jnienv, + AddLocalRef<jobject>(jnienv, + field->GetDeclaringClass().Ptr())); + char type_char = art::Primitive::Descriptor(field->GetTypeAsPrimitiveType())[0]; + jvalue val; + // 64bit integer is the largest value in the union so we should be fine simply copying it into + // the union. + val.j = field_value.GetJ(); + RunEventCallback<ArtJvmtiEvent::kFieldModification>( + self, + jnienv, + art::jni::EncodeArtMethod(method), + static_cast<jlocation>(dex_pc), + static_cast<jclass>(fklass.get()), + field->IsStatic() ? nullptr : this_ref.get(), // nb static field modification get given + // the class as this_object for some + // reason. + art::jni::EncodeArtField(field), + type_char, + val); + } } // Call-back when an exception is caught. @@ -490,15 +559,20 @@ static uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) { case ArtJvmtiEvent::kMethodExit: return art::instrumentation::Instrumentation::kMethodExited | art::instrumentation::Instrumentation::kMethodUnwind; + case ArtJvmtiEvent::kFieldModification: + return art::instrumentation::Instrumentation::kFieldWritten; + case ArtJvmtiEvent::kFieldAccess: + return art::instrumentation::Instrumentation::kFieldRead; default: LOG(FATAL) << "Unknown event "; return 0; } } -static void SetupMethodTraceListener(JvmtiMethodTraceListener* listener, - ArtJvmtiEvent event, - bool enable) { +static void SetupTraceListener(JvmtiMethodTraceListener* listener, + ArtJvmtiEvent event, + bool enable) { + art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative); uint32_t new_events = GetInstrumentationEventsFor(event); art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation(); art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(), @@ -529,7 +603,9 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kMethodEntry: case ArtJvmtiEvent::kMethodExit: - SetupMethodTraceListener(method_trace_listener_.get(), event, enable); + case ArtJvmtiEvent::kFieldAccess: + case ArtJvmtiEvent::kFieldModification: + SetupTraceListener(method_trace_listener_.get(), event, enable); return; default: diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc index 342d8be2b0..32c064e89c 100644 --- a/runtime/openjdkjvmti/ti_field.cc +++ b/runtime/openjdkjvmti/ti_field.cc @@ -187,4 +187,68 @@ jvmtiError FieldUtil::IsFieldSynthetic(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError FieldUtil::SetFieldModificationWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { + ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + if (field == nullptr) { + return ERR(INVALID_FIELDID); + } + auto res_pair = env->modify_watched_fields.insert(art::jni::DecodeArtField(field)); + if (!res_pair.second) { + // Didn't get inserted because it's already present! + return ERR(DUPLICATE); + } + return OK; +} + +jvmtiError FieldUtil::ClearFieldModificationWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { + ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + if (field == nullptr) { + return ERR(INVALID_FIELDID); + } + auto pos = env->modify_watched_fields.find(art::jni::DecodeArtField(field)); + if (pos == env->modify_watched_fields.end()) { + return ERR(NOT_FOUND); + } + env->modify_watched_fields.erase(pos); + return OK; +} + +jvmtiError FieldUtil::SetFieldAccessWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { + ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + if (field == nullptr) { + return ERR(INVALID_FIELDID); + } + auto res_pair = env->access_watched_fields.insert(art::jni::DecodeArtField(field)); + if (!res_pair.second) { + // Didn't get inserted because it's already present! + return ERR(DUPLICATE); + } + return OK; +} + +jvmtiError FieldUtil::ClearFieldAccessWatch(jvmtiEnv* jenv, jclass klass, jfieldID field) { + ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + if (field == nullptr) { + return ERR(INVALID_FIELDID); + } + auto pos = env->access_watched_fields.find(art::jni::DecodeArtField(field)); + if (pos == env->access_watched_fields.end()) { + return ERR(NOT_FOUND); + } + env->access_watched_fields.erase(pos); + return OK; +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_field.h b/runtime/openjdkjvmti/ti_field.h index 9a29f81d76..880949eecb 100644 --- a/runtime/openjdkjvmti/ti_field.h +++ b/runtime/openjdkjvmti/ti_field.h @@ -60,6 +60,11 @@ class FieldUtil { jclass klass, jfieldID field, jboolean* is_synthetic_ptr); + + static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field); + static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field); + static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field); + static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field); }; } // namespace openjdkjvmti |