diff options
Diffstat (limited to 'openjdkjvmti/events.cc')
-rw-r--r-- | openjdkjvmti/events.cc | 141 |
1 files changed, 96 insertions, 45 deletions
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 22c622adcf..b41edc5e8c 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -794,6 +794,12 @@ class JvmtiMethodTraceListener final : public art::instrumentation::Instrumentat void WatchedFramePop(art::Thread* self, const art::ShadowFrame& frame) REQUIRES_SHARED(art::Locks::mutator_lock_) override { art::JNIEnvExt* jnienv = self->GetJniEnv(); + // Remove the force-interpreter added by the WatchFrame. + { + art::MutexLock mu(self, *art::Locks::thread_list_lock_); + CHECK_GT(self->ForceInterpreterCount(), 0u); + self->DecrementForceInterpreterCount(); + } jboolean is_exception_pending = self->IsExceptionPending(); RunEventCallback<ArtJvmtiEvent::kFramePop>( event_handler_, @@ -965,45 +971,78 @@ static uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) { } } -static bool EventNeedsFullDeopt(ArtJvmtiEvent event) { +enum class DeoptRequirement { + // Limited/no deopt required. + kLimited, + // A single thread must be put into interpret only. + kThread, + // All methods and all threads deopted. + kFull, +}; + +static DeoptRequirement GetDeoptRequirement(ArtJvmtiEvent event, jthread thread) { switch (event) { case ArtJvmtiEvent::kBreakpoint: case ArtJvmtiEvent::kException: - return false; - // TODO We should support more of these or at least do something to make them discriminate by - // thread. + return DeoptRequirement::kLimited; + // TODO MethodEntry is needed due to inconsistencies between the interpreter and the trampoline + // in how to handle exceptions. case ArtJvmtiEvent::kMethodEntry: case ArtJvmtiEvent::kExceptionCatch: + return DeoptRequirement::kFull; case ArtJvmtiEvent::kMethodExit: case ArtJvmtiEvent::kFieldModification: case ArtJvmtiEvent::kFieldAccess: case ArtJvmtiEvent::kSingleStep: case ArtJvmtiEvent::kFramePop: - return true; + return thread == nullptr ? DeoptRequirement::kFull : DeoptRequirement::kThread; default: LOG(FATAL) << "Unexpected event type!"; UNREACHABLE(); } } -void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, - ArtJvmtiEvent event, - bool enable) { - bool needs_full_deopt = EventNeedsFullDeopt(event); +jvmtiError EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, + ArtJvmtiEvent event, + jthread thread, + bool enable) { + DeoptRequirement deopt_req = GetDeoptRequirement(event, thread); // Make sure we can deopt. { art::ScopedObjectAccess soa(art::Thread::Current()); DeoptManager* deopt_manager = DeoptManager::Get(); + jvmtiError err = OK; if (enable) { deopt_manager->AddDeoptimizationRequester(); - if (needs_full_deopt) { - deopt_manager->AddDeoptimizeAllMethods(); + switch (deopt_req) { + case DeoptRequirement::kFull: + deopt_manager->AddDeoptimizeAllMethods(); + break; + case DeoptRequirement::kThread: + err = deopt_manager->AddDeoptimizeThreadMethods(soa, thread); + break; + default: + break; + } + if (err != OK) { + deopt_manager->RemoveDeoptimizationRequester(); + return err; } } else { - if (needs_full_deopt) { - deopt_manager->RemoveDeoptimizeAllMethods(); + switch (deopt_req) { + case DeoptRequirement::kFull: + deopt_manager->RemoveDeoptimizeAllMethods(); + break; + case DeoptRequirement::kThread: + err = deopt_manager->RemoveDeoptimizeThreadMethods(soa, thread); + break; + default: + break; } deopt_manager->RemoveDeoptimizationRequester(); + if (err != OK) { + return err; + } } } @@ -1019,7 +1058,7 @@ void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, if (IsEventEnabledAnywhere(other)) { // The event needs to be kept around/is already enabled by the other jvmti event that uses the // same instrumentation event. - return; + return OK; } } art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative); @@ -1030,6 +1069,7 @@ void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, } else { instr->RemoveListener(listener, new_events); } + return OK; } // Makes sure that all compiled methods are AsyncDeoptimizable so we can deoptimize (and force to @@ -1085,41 +1125,42 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { return false; } -void EventHandler::SetupFramePopTraceListener(bool enable) { +jvmtiError EventHandler::SetupFramePopTraceListener(jthread thread, bool enable) { if (enable) { frame_pop_enabled = true; - SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable); + return SetupTraceListener( + method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, thread, enable); } else { // remove the listener if we have no outstanding frames. { art::ReaderMutexLock mu(art::Thread::Current(), envs_lock_); - for (ArtJvmTiEnv* env : envs) { + for (ArtJvmTiEnv *env : envs) { art::ReaderMutexLock event_mu(art::Thread::Current(), env->event_info_mutex_); if (!env->notify_frames.empty()) { // Leaving FramePop listener since there are unsent FramePop events. - return; + return OK; } } frame_pop_enabled = false; } - SetupTraceListener(method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, enable); + return SetupTraceListener( + method_trace_listener_.get(), ArtJvmtiEvent::kFramePop, thread, enable); } } // Handle special work for the given event type, if necessary. -void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { +jvmtiError EventHandler::HandleEventType(ArtJvmtiEvent event, jthread thread, bool enable) { switch (event) { case ArtJvmtiEvent::kDdmPublishChunk: SetupDdmTracking(ddm_listener_.get(), enable); - return; + return OK; case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); - return; - + return OK; case ArtJvmtiEvent::kGarbageCollectionStart: case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); - return; + return OK; // FramePop can never be disabled once it's been turned on if it was turned off with outstanding // pop-events since we would either need to deal with dangling pointers or have missed events. case ArtJvmtiEvent::kFramePop: @@ -1127,8 +1168,7 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { // The frame-pop event was held on by pending events so we don't need to do anything. break; } else { - SetupFramePopTraceListener(enable); - break; + return SetupFramePopTraceListener(thread, enable); } case ArtJvmtiEvent::kMethodEntry: case ArtJvmtiEvent::kMethodExit: @@ -1138,8 +1178,7 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kExceptionCatch: case ArtJvmtiEvent::kBreakpoint: case ArtJvmtiEvent::kSingleStep: - SetupTraceListener(method_trace_listener_.get(), event, enable); - return; + return SetupTraceListener(method_trace_listener_.get(), event, thread, enable); case ArtJvmtiEvent::kMonitorContendedEnter: case ArtJvmtiEvent::kMonitorContendedEntered: case ArtJvmtiEvent::kMonitorWait: @@ -1147,10 +1186,11 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { if (!OtherMonitorEventsEnabledAnywhere(event)) { SetupMonitorListener(monitor_listener_.get(), park_listener_.get(), enable); } - return; + return OK; default: break; } + return OK; } // Checks to see if the env has the capabilities associated with the given event. @@ -1212,21 +1252,9 @@ static bool HasAssociatedCapability(ArtJvmTiEnv* env, } jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, - art::Thread* thread, + jthread thread, ArtJvmtiEvent event, jvmtiEventMode mode) { - if (thread != nullptr) { - art::ThreadState state = thread->GetState(); - if (state == art::ThreadState::kStarting || - state == art::ThreadState::kTerminated || - thread->IsStillStarting()) { - return ERR(THREAD_NOT_ALIVE); - } - if (!IsThreadControllable(event)) { - return ERR(ILLEGAL_ARGUMENT); - } - } - if (mode != JVMTI_ENABLE && mode != JVMTI_DISABLE) { return ERR(ILLEGAL_ARGUMENT); } @@ -1239,6 +1267,28 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, return ERR(MUST_POSSESS_CAPABILITY); } + art::Thread* art_thread = nullptr; + if (thread != nullptr) { + if (!IsThreadControllable(event)) { + return ERR(ILLEGAL_ARGUMENT); + } + art::ScopedObjectAccess soa(art::Thread::Current()); + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + jvmtiError err = ERR(INTERNAL); + if (!ThreadUtil::GetAliveNativeThread(thread, soa, &art_thread, &err)) { + return err; + } else if (art_thread->IsStillStarting()) { + return ERR(THREAD_NOT_ALIVE); + } + art::ThreadState state = art_thread->GetState(); + if (state == art::ThreadState::kStarting || state == art::ThreadState::kTerminated) { + return ERR(THREAD_NOT_ALIVE); + } + } + + // TODO We use art_thread simply as a global unique identifier here. It is not safe to actually + // use it without holding the thread_list_lock_. + bool old_state; bool new_state; @@ -1249,13 +1299,14 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, art::WriterMutexLock mu_env_info(self, env->event_info_mutex_); old_state = global_mask.Test(event); if (mode == JVMTI_ENABLE) { - env->event_masks.EnableEvent(env, thread, event); + env->event_masks.EnableEvent(env, art_thread, event); global_mask.Set(event); new_state = true; } else { DCHECK_EQ(mode, JVMTI_DISABLE); - env->event_masks.DisableEvent(env, thread, event); + // TODO Replace art_thread with a uintptr_t or something to indicate we cannot read from it. + env->event_masks.DisableEvent(env, art_thread, event); RecalculateGlobalEventMaskLocked(event); new_state = global_mask.Test(event); } @@ -1263,7 +1314,7 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, // Handle any special work required for the event type. if (new_state != old_state) { - HandleEventType(event, mode == JVMTI_ENABLE); + return HandleEventType(event, thread, mode == JVMTI_ENABLE); } return ERR(NONE); |