[debugger] Add proper processing of explicitly resumed threads

GitOrigin-RevId: 85cfa795069482bb32c50f3d35b99238f2f9eb98
This commit is contained in:
Alexey Merkulov
2024-05-22 19:20:36 +02:00
committed by intellij-monorepo-bot
parent 886c4f6bc0
commit cc17be8830
3 changed files with 64 additions and 13 deletions

View File

@@ -778,7 +778,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
}
boolean noStandardSuspendNeeded;
if (DebuggerUtils.isAlwaysSuspendThreadBeforeSwitch()) {
noStandardSuspendNeeded = specialSuspendProcessingForAlwaysSwitch(suspendContext, requestor, suspendManager, thread);
noStandardSuspendNeeded = specialSuspendProcessingForAlwaysSwitch(suspendContext, requestor, (SuspendManagerImpl)suspendManager, thread);
}
else {
noStandardSuspendNeeded = requestor instanceof CustomProcessingLocatableEventRequestor customRequestor &&
@@ -792,7 +792,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
private static boolean specialSuspendProcessingForAlwaysSwitch(@NotNull SuspendContextImpl suspendContext,
@NotNull LocatableEventRequestor requestor,
@NotNull SuspendManager suspendManager,
@NotNull SuspendManagerImpl suspendManager,
@NotNull ThreadReference thread) {
if (suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
if (!(requestor instanceof SuspendOtherThreadsRequestor)) {
@@ -815,15 +815,44 @@ public class DebugProcessEvents extends DebugProcessImpl {
afterSwitch = c -> true;
}
boolean noStandardSuspendNeeded;
if (ContainerUtil.exists(suspendManager.getEventContexts(), c -> c.mySuspendAllSwitchedContext)) {
List<SuspendContextImpl> suspendAllContexts =
ContainerUtil.filter(suspendManager.getEventContexts(), c -> c.getSuspendPolicy() == EventRequest.SUSPEND_ALL);
if (!suspendAllContexts.isEmpty()) {
if (suspendAllContexts.size() > 1) {
LOG.error("Many suspend all switch contexts: " + suspendAllContexts);
}
// Already stopped, so this is "remaining" event. Need to resume the event.
noStandardSuspendNeeded = true;
if (thread.suspendCount() == 1) {
// There are some errors in evaluation-resume-suspend logic
LOG.error("This means resuming this thead to the running state");
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
ThreadReferenceProxyImpl threadProxy = debugProcess.getVirtualMachineProxy().getThreadReferenceProxy(thread);
if (suspendManager.myExplicitlyResumedThreads.contains(threadProxy)) {
for (SuspendContextImpl context : suspendManager.getEventContexts()) {
if (context.getSuspendPolicy() == EventRequest.SUSPEND_ALL && !context.suspends(threadProxy)) {
suspendManager.suspendThread(context, threadProxy);
}
}
suspendManager.myExplicitlyResumedThreads.remove(threadProxy);
suspendManager.voteResume(suspendContext);
SuspendContextImpl suspendAllContext = suspendAllContexts.get(0);
debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(suspendAllContext) {
@Override
public void contextAction(@NotNull SuspendContextImpl c) {
DebuggerSession session = debugProcess.getSession();
DebuggerContextImpl debuggerContext = DebuggerContextImpl.createDebuggerContext(session, suspendAllContext, threadProxy, null);
DebuggerInvocationUtil.invokeLater(debugProcess.getProject(),
() -> session.getContextManager().setState(debuggerContext, DebuggerSession.State.PAUSED, DebuggerSession.Event.CONTEXT, null));
}
});
}
else {
if (thread.suspendCount() == 1) {
// There are some errors in evaluation-resume-suspend logic
LOG.error("This means resuming this thead to the running state");
}
suspendManager.voteResume(suspendContext);
debugProcess.notifyStoppedOtherThreads();
}
suspendManager.voteResume(suspendContext);
suspendContext.getDebugProcess().notifyStoppedOtherThreads();
}
else {
noStandardSuspendNeeded = SuspendOtherThreadsRequestor.initiateTransferToSuspendAll(suspendContext, afterSwitch);

View File

@@ -180,7 +180,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
public void paused(@NotNull SuspendContext suspendContext) {
boolean isSuspendAll = suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL;
if (isSuspendAll && DebuggerUtils.isNewThreadSuspendStateTracking()) {
resumeThreadsUnderEvaluationAfterPause((SuspendContextImpl)suspendContext);
resumeThreadsUnderEvaluationAndExplicitlyResumedAfterPause((SuspendContextImpl)suspendContext);
}
myThreadBlockedMonitor.stopWatching(!isSuspendAll ? suspendContext.getThread() : null);
@@ -1099,7 +1099,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
}
private void resumeThreadsUnderEvaluationAfterPause(@NotNull SuspendContextImpl suspendAllContext) {
private void resumeThreadsUnderEvaluationAndExplicitlyResumedAfterPause(@NotNull SuspendContextImpl suspendAllContext) {
for (SuspendContextImpl suspendContext : mySuspendManager.getEventContexts()) {
EvaluationContextImpl evaluationContext = suspendContext.getEvaluationContext();
if (evaluationContext != null) {
@@ -1117,6 +1117,17 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
}
}
}
List<ThreadReferenceProxyImpl> threads = new ArrayList<>(mySuspendManager.myExplicitlyResumedThreads);
for (ThreadReferenceProxyImpl thread : threads) {
if (thread == suspendAllContext.getEventThread()) {
// It seems, it is stopped on the breakpoint on this explicitly resumed thread
mySuspendManager.myExplicitlyResumedThreads.remove(thread);
continue;
}
if (!suspendAllContext.suspends(thread)) { // the previous loop can theoretically resume it already
mySuspendManager.resumeThread(suspendAllContext, thread);
}
}
}
private abstract class InvokeCommand<E extends Value> {
@@ -1241,7 +1252,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
}
finally {
myEvaluationContext.setThreadForEvaluation(null);
if (DebuggerUtils.isNewThreadSuspendStateTracking()) {
if (DebuggerUtils.isNewThreadSuspendStateTracking() && !mySuspendManager.myExplicitlyResumedThreads.contains(invokeThread)) {
for (SuspendContextImpl anotherContext : mySuspendManager.getEventContexts()) {
if (anotherContext != suspendContext && !anotherContext.suspends(invokeThread)) {
boolean shouldSuspendThread = false;
@@ -2143,14 +2154,16 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
return;
}
final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread);
final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(mySuspendManager, myThread);
for (SuspendContextImpl suspendContext : suspendingContexts) {
if (suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD && suspendContext.getEventThread() == myThread) {
getSession().getXDebugSession().sessionResumed();
getManagerThread().invoke(createResumeCommand(suspendContext));
}
else {
getSuspendManager().resumeThread(suspendContext, myThread);
DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().removeThreadFilter(context.getDebugProcess());
mySuspendManager.resumeThread(suspendContext, myThread);
mySuspendManager.myExplicitlyResumedThreads.add(myThread);
}
}
}

View File

@@ -32,6 +32,8 @@ public class SuspendManagerImpl implements SuspendManager {
private final Deque<SuspendContextImpl> myPausedContexts = new ConcurrentLinkedDeque<>();
private final Set<ThreadReferenceProxyImpl> myFrozenThreads = ConcurrentCollectionFactory.createConcurrentSet();
protected final Set<ThreadReferenceProxyImpl> myExplicitlyResumedThreads = ConcurrentCollectionFactory.createConcurrentSet();
private final DebugProcessImpl myDebugProcess;
private int suspends = 0;
@@ -148,6 +150,13 @@ public class SuspendManagerImpl implements SuspendManager {
myDebugProcess.logThreads();
popContext(context);
if (context.getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
if (!ContainerUtil.exists(myPausedContexts, c -> c.getSuspendPolicy() == EventRequest.SUSPEND_ALL)) {
myExplicitlyResumedThreads.clear();
} else if (eventThread != null && !ContainerUtil.exists(myEventContexts, c -> c.suspends(eventThread))) {
myExplicitlyResumedThreads.add(eventThread);
}
}
Set<ThreadReferenceProxyImpl> resumedThreads = context.myResumedThreads;
if (resumedThreads != null) {
for (ThreadReferenceProxyImpl thread : resumedThreads) {