From b7c640d364d32b79cb52d04750b063667a9a0c86 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 20 Mar 2019 15:52:13 -0700 Subject: JVMTI Force early return Add support for can_force_early_return jvmti capability. This allows one to force java frames to exit early. Exited frames have all of their normal locks released. We implement this by modifying the existing method exit events to allow one to modify the exit value during the callback. This is used to implement ForceEarlyReturn by adding internal-only events that will change the return value of methods once they return (using kForcePopFrame) avoiding the need to modify the actual interpreter very deeply. This also makes it simple to continue to use the standard deoptimization functions to force the actual return. In order to simplify book-keeping the internal event is refcounted, not associated with any specific jvmtiEnv, and only settable on specific threads. The internal event is added by the ForceEarlyReturn function and then removed by the MethodExit event when we update the return value. Bug: 130028055 Test: ./test.py --host Change-Id: Ifa44605b4e8032605f503a654ddf4bd2fc6b60bf --- openjdkjvmti/events.h | 76 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 5 deletions(-) (limited to 'openjdkjvmti/events.h') diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index d54c87aceb..ac86d0cd9b 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -18,14 +18,17 @@ #define ART_OPENJDKJVMTI_EVENTS_H_ #include +#include #include #include #include +#include "android-base/thread_annotations.h" #include "base/macros.h" #include "base/mutex.h" #include "jvmti.h" +#include "managed_stack.h" #include "thread.h" namespace openjdkjvmti { @@ -73,17 +76,43 @@ enum class ArtJvmtiEvent : jint { kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, kObjectFree = JVMTI_EVENT_OBJECT_FREE, kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, + // Internal event to mark a ClassFileLoadHook as one created with the can_retransform_classes + // capability. kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2, - kMaxEventTypeVal = kDdmPublishChunk, + kMaxNormalEventTypeVal = kDdmPublishChunk, + + // All that follow are events used to implement internal JVMTI functions. They are not settable + // directly by agents. + kMinInternalEventTypeVal = kMaxNormalEventTypeVal + 1, + + // Internal event we use to implement the ForceEarlyReturn functions. + kForceEarlyReturnUpdateReturnValue = kMinInternalEventTypeVal, + kMaxInternalEventTypeVal = kForceEarlyReturnUpdateReturnValue, + + kMaxEventTypeVal = kMaxInternalEventTypeVal, }; +constexpr jint kInternalEventCount = static_cast(ArtJvmtiEvent::kMaxInternalEventTypeVal) - + static_cast(ArtJvmtiEvent::kMinInternalEventTypeVal) + 1; + using ArtJvmtiEventDdmPublishChunk = void (*)(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jint data_type, jint data_len, const jbyte* data); +// It is not enough to store a Thread pointer, as these may be reused. Use the pointer and the +// thread id. +// Note: We could just use the tid like tracing does. +using UniqueThread = std::pair; + +struct UniqueThreadHasher { + std::size_t operator()(const UniqueThread& k) const { + return std::hash{}(k.second) ^ (std::hash{}(k.first) << 1); + } +}; + struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr) { memset(this, 0, sizeof(jvmtiEventCallbacks)); @@ -141,10 +170,6 @@ struct EventMasks { // The per-thread enabled events. - // It is not enough to store a Thread pointer, as these may be reused. Use the pointer and the - // thread id. - // Note: We could just use the tid like tracing does. - using UniqueThread = std::pair; // TODO: Native thread objects are immovable, so we can use them as keys in an (unordered) map, // if necessary. std::vector> thread_event_masks; @@ -198,6 +223,16 @@ class EventHandler { return global_mask.Test(event); } + // Sets an internal event. Unlike normal JVMTI events internal events are not associated with any + // particular jvmtiEnv and are refcounted. This refcounting is done to allow us to easily enable + // events during functions and disable them during the requested event callback. Since these are + // used to implement various JVMTI functions these events always have a single target thread. If + // target is null the current thread is used. + jvmtiError SetInternalEvent(jthread target, + ArtJvmtiEvent event, + jvmtiEventMode mode) + REQUIRES(!envs_lock_, !art::Locks::mutator_lock_); + jvmtiError SetEvent(ArtJvmTiEnv* env, jthread thread, ArtJvmtiEvent event, @@ -246,9 +281,15 @@ class EventHandler { inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const REQUIRES(!envs_lock_); + void AddDelayedNonStandardExitEvent(const art::ShadowFrame* frame, bool is_object, jvalue val) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(art::Locks::user_code_suspension_lock_, art::Locks::thread_list_lock_); + private: void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable); + uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event); + // Specifically handle the FramePop event which it might not always be possible to turn off. void SetupFramePopTraceListener(bool enable); @@ -325,6 +366,21 @@ class EventHandler { bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event); + int32_t GetInternalEventRefcount(ArtJvmtiEvent event) const REQUIRES(envs_lock_); + // Increment internal event refcount for the given event and return the new count. + int32_t IncrInternalEventRefcount(ArtJvmtiEvent event) REQUIRES(envs_lock_); + // Decrement internal event refcount for the given event and return the new count. + int32_t DecrInternalEventRefcount(ArtJvmtiEvent event) REQUIRES(envs_lock_); + + int32_t& GetInternalEventThreadRefcount(ArtJvmtiEvent event, art::Thread* target) + REQUIRES(envs_lock_, art::Locks::thread_list_lock_); + // Increment internal event refcount for the given event and return the new count. + int32_t IncrInternalEventThreadRefcount(ArtJvmtiEvent event, art::Thread* target) + REQUIRES(envs_lock_, art::Locks::thread_list_lock_); + // Decrement internal event refcount for the given event and return the new count. + int32_t DecrInternalEventThreadRefcount(ArtJvmtiEvent event, art::Thread* target) + REQUIRES(envs_lock_, art::Locks::thread_list_lock_); + // List of all JvmTiEnv objects that have been created, in their creation order. It is a std::list // since we mostly access it by iterating over the entire thing, only ever append to the end, and // need to be able to remove arbitrary elements from it. @@ -348,6 +404,16 @@ class EventHandler { // continue to listen to this event even if it has been disabled. // TODO We could remove the listeners once all jvmtiEnvs have drained their shadow-frame vectors. bool frame_pop_enabled; + + // The overall refcount for each internal event across all threads. + std::array internal_event_refcount_ GUARDED_BY(envs_lock_); + // The refcount for each thread for each internal event. + // TODO We should clean both this and the normal EventMask lists up when threads end. + std::array, kInternalEventCount> + internal_event_thread_refcount_ + GUARDED_BY(envs_lock_) GUARDED_BY(art::Locks::thread_list_lock_); + + friend class JvmtiMethodTraceListener; }; } // namespace openjdkjvmti -- cgit v1.2.3-59-g8ed1b