Fix GetThreadState with threads in kNative.
We were always returning JVMTI_THREAD_STATE_WAITING for threads in the
kNative state. To prevent any similar problems from happening in the
future we changed it so all thread-states are explicitly enumerated
and handled in a switch statement.
Test: ./test.py --host -j50
Bug: 67784165
Change-Id: I6646b36aa36cb4671bf95777aefc5c88b659e90f
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 9a809df..d0913cf 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -340,47 +340,92 @@
jint jvmti_state = JVMTI_THREAD_STATE_ALIVE;
if (state.thread_user_code_suspend_count != 0) {
+ // Suspended can be set with any thread state so check it here. Even if the thread isn't in
+ // kSuspended state it will move to that once it hits a checkpoint so we can still set this.
jvmti_state |= JVMTI_THREAD_STATE_SUSPENDED;
// Note: We do not have data about the previous state. Otherwise we should load the previous
// state here.
}
if (state.native_thread->IsInterrupted()) {
+ // Interrupted can be set with any thread state so check it here.
jvmti_state |= JVMTI_THREAD_STATE_INTERRUPTED;
}
- if (internal_thread_state == art::ThreadState::kNative) {
- jvmti_state |= JVMTI_THREAD_STATE_IN_NATIVE;
+ // Enumerate all the thread states and fill in the other bits. This contains the results of
+ // following the decision tree in the JVMTI spec GetThreadState documentation.
+ switch (internal_thread_state) {
+ case art::ThreadState::kRunnable:
+ case art::ThreadState::kWaitingWeakGcRootRead:
+ case art::ThreadState::kSuspended:
+ // These are all simply runnable.
+ // kRunnable is self-explanatory.
+ // kWaitingWeakGcRootRead is set during some operations with strings due to the intern-table
+ // so we want to keep it marked as runnable.
+ // kSuspended we don't mark since if we don't have a user_code_suspend_count then it is done
+ // by the GC and not a JVMTI suspension, which means it cannot be removed by ResumeThread.
+ jvmti_state |= JVMTI_THREAD_STATE_RUNNABLE;
+ break;
+ case art::ThreadState::kNative:
+ // kNative means native and runnable. Technically THREAD_STATE_IN_NATIVE can be set with any
+ // state but we don't have the information to know if it should be present for any but the
+ // kNative state.
+ jvmti_state |= (JVMTI_THREAD_STATE_IN_NATIVE |
+ JVMTI_THREAD_STATE_RUNNABLE);
+ break;
+ case art::ThreadState::kBlocked:
+ // Blocked is one of the top level states so it sits alone.
+ jvmti_state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
+ break;
+ case art::ThreadState::kWaiting:
+ // Object.wait() so waiting, indefinitely, in object.wait.
+ jvmti_state |= (JVMTI_THREAD_STATE_WAITING |
+ JVMTI_THREAD_STATE_WAITING_INDEFINITELY |
+ JVMTI_THREAD_STATE_IN_OBJECT_WAIT);
+ break;
+ case art::ThreadState::kTimedWaiting:
+ // Object.wait(long) so waiting, with timeout, in object.wait.
+ jvmti_state |= (JVMTI_THREAD_STATE_WAITING |
+ JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
+ JVMTI_THREAD_STATE_IN_OBJECT_WAIT);
+ break;
+ case art::ThreadState::kSleeping:
+ // In object.sleep. This is a timed wait caused by sleep.
+ jvmti_state |= (JVMTI_THREAD_STATE_WAITING |
+ JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
+ JVMTI_THREAD_STATE_SLEEPING);
+ break;
+ // TODO We might want to print warnings if we have the debugger running while JVMTI agents are
+ // attached.
+ case art::ThreadState::kWaitingForDebuggerSend:
+ case art::ThreadState::kWaitingForDebuggerToAttach:
+ case art::ThreadState::kWaitingInMainDebuggerLoop:
+ case art::ThreadState::kWaitingForDebuggerSuspension:
+ case art::ThreadState::kWaitingForLockInflation:
+ case art::ThreadState::kWaitingForTaskProcessor:
+ case art::ThreadState::kWaitingForGcToComplete:
+ case art::ThreadState::kWaitingForCheckPointsToRun:
+ case art::ThreadState::kWaitingPerformingGc:
+ case art::ThreadState::kWaitingForJniOnLoad:
+ case art::ThreadState::kWaitingInMainSignalCatcherLoop:
+ case art::ThreadState::kWaitingForSignalCatcherOutput:
+ case art::ThreadState::kWaitingForDeoptimization:
+ case art::ThreadState::kWaitingForMethodTracingStart:
+ case art::ThreadState::kWaitingForVisitObjects:
+ case art::ThreadState::kWaitingForGetObjectsAllocated:
+ case art::ThreadState::kWaitingForGcThreadFlip:
+ // All of these are causing the thread to wait for an indeterminate amount of time but isn't
+ // caused by sleep, park, or object#wait.
+ jvmti_state |= (JVMTI_THREAD_STATE_WAITING |
+ JVMTI_THREAD_STATE_WAITING_INDEFINITELY);
+ break;
+ case art::ThreadState::kStarting:
+ case art::ThreadState::kTerminated:
+ // We only call this if we are alive so we shouldn't see either of these states.
+ LOG(FATAL) << "Should not be in state " << internal_thread_state;
+ UNREACHABLE();
}
-
- if (internal_thread_state == art::ThreadState::kRunnable ||
- internal_thread_state == art::ThreadState::kWaitingWeakGcRootRead ||
- internal_thread_state == art::ThreadState::kSuspended) {
- jvmti_state |= JVMTI_THREAD_STATE_RUNNABLE;
- } else if (internal_thread_state == art::ThreadState::kBlocked) {
- jvmti_state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
- } else {
- // Should be in waiting state.
- jvmti_state |= JVMTI_THREAD_STATE_WAITING;
-
- if (internal_thread_state == art::ThreadState::kTimedWaiting ||
- internal_thread_state == art::ThreadState::kSleeping) {
- jvmti_state |= JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT;
- } else {
- jvmti_state |= JVMTI_THREAD_STATE_WAITING_INDEFINITELY;
- }
-
- if (internal_thread_state == art::ThreadState::kSleeping) {
- jvmti_state |= JVMTI_THREAD_STATE_SLEEPING;
- }
-
- if (internal_thread_state == art::ThreadState::kTimedWaiting ||
- internal_thread_state == art::ThreadState::kWaiting) {
- jvmti_state |= JVMTI_THREAD_STATE_IN_OBJECT_WAIT;
- }
-
- // TODO: PARKED. We'll have to inspect the stack.
- }
+ // TODO: PARKED. We'll have to inspect the stack.
return jvmti_state;
}