summaryrefslogtreecommitdiff
path: root/openjdkjvmti/events.cc
diff options
context:
space:
mode:
Diffstat (limited to 'openjdkjvmti/events.cc')
-rw-r--r--openjdkjvmti/events.cc141
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);