8332738: Debug agent can deadlock on callbackLock when using StackFrame.PopFrames

Reviewed-by: sspitsyn, amenkov
This commit is contained in:
Chris Plummer 2024-07-29 16:55:38 +00:00
parent c23d37e10a
commit c4e6255ac3
5 changed files with 41 additions and 15 deletions

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -93,8 +93,8 @@ static jrawMonitorID callbackBlock;
* not blocking might mean that a return would continue execution of
* some java thread in the middle of VM_DEATH, this seems troubled.
*
* WARNING: No not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK
* block, this will mess up the count.
* WARNING: Do not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK
* block. This will mess up the active_callbacks count.
*/
#define BEGIN_CALLBACK() \
@ -1709,6 +1709,18 @@ eventHandler_unlock(void)
debugMonitorExit(handlerLock);
}
void
callback_lock(void)
{
debugMonitorEnter(callbackLock);
}
void
callback_unlock(void)
{
debugMonitorExit(callbackLock);
}
/***** handler creation *****/
HandlerNode *

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -78,6 +78,9 @@ void eventHandler_waitForActiveCallbacks();
void eventHandler_lock(void);
void eventHandler_unlock(void);
void callback_lock(void);
void callback_unlock(void);
jboolean eventHandler_synthesizeUnloadEvent(char *signature, JNIEnv *env);
jclass getMethodClass(jvmtiEnv *jvmti_env, jmethodID method);

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -719,7 +719,9 @@ invoker_completeInvokeRequest(jthread thread)
exc = NULL;
id = 0;
eventHandler_lock(); /* for proper lock order */
callback_lock(); /* for proper lock order in threadControl getLocks() */
eventHandler_lock(); /* for proper lock order in threadControl getLocks() */
stepControl_lock(); /* for proper lock order in threadControl getLocks() */
debugMonitorEnter(invokerLock);
request = threadControl_getInvokeRequest(thread);
@ -772,7 +774,7 @@ invoker_completeInvokeRequest(jthread thread)
* We cannot delete saved exception or return value references
* since otherwise a deleted handle would escape when writing
* the response to the stream. Instead, we clean those refs up
* after writing the respone.
* after writing the response.
*/
deleteGlobalArgumentRefs(env, request);
@ -790,7 +792,9 @@ invoker_completeInvokeRequest(jthread thread)
* Give up the lock before I/O operation
*/
debugMonitorExit(invokerLock);
stepControl_unlock();
eventHandler_unlock();
callback_unlock();
if (!detached) {
outStream_initReply(&out, id);

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -805,7 +805,8 @@ stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth,
LOG_STEP(("stepControl_beginStep: thread=%p,size=%d,depth=%d",
thread, size, depth));
eventHandler_lock(); /* for proper lock order */
callback_lock(); /* for proper lock order in threadControl getLocks() */
eventHandler_lock(); /* for proper lock order in threadControl getLocks() */
stepControl_lock();
step = threadControl_getStepRequest(thread);
@ -852,6 +853,7 @@ stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth,
stepControl_unlock();
eventHandler_unlock();
callback_unlock();
return error;
}

@ -642,26 +642,31 @@ getLocks(void)
* thread) needs to be grabbed here. This allows thread control
* code to safely suspend and resume the application threads
* while ensuring they don't hold a critical lock.
*
* stepControl_beginStep() grabs the eventHandler lock and stepControl lock
* before eventually ending up here, so we need to maintain that order here.
* Similarly, invoker_completeInvokeRequest() grabs the eventHandler lock
* and invoker lock.
*/
callback_lock();
eventHandler_lock();
stepControl_lock();
invoker_lock();
eventHelper_lock();
stepControl_lock();
commonRef_lock();
debugMonitorEnter(threadLock);
commonRef_lock();
}
static void
releaseLocks(void)
{
debugMonitorExit(threadLock);
commonRef_unlock();
stepControl_unlock();
debugMonitorExit(threadLock);
eventHelper_unlock();
invoker_unlock();
stepControl_unlock();
eventHandler_unlock();
callback_unlock();
}
void