[debugger] IDEA-338723 Introduce thread-like abstraction

Next commits will use it to filter coroutines during stepping

IJ-CR-120446

GitOrigin-RevId: 9589c094695181abebc235846cf521358414bf7c
This commit is contained in:
Alexey Merkulov
2023-11-21 17:25:06 +01:00
committed by intellij-monorepo-bot
parent cd7dc69252
commit 9e5b4cf39d
7 changed files with 76 additions and 22 deletions

View File

@@ -31,7 +31,6 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.registry.Registry;
@@ -641,8 +640,8 @@ public class DebugProcessEvents extends DebugProcessImpl {
// because these evaluations may lead to skipping of more important stepping events,
// see IDEA-336282.
if (!DebuggerSession.filterBreakpointsDuringSteppingUsingDebuggerEngine()) {
ThreadReference filteredThread = getRequestsManager().getFilterThread();
if (filteredThread != null && !Comparing.equal(filteredThread, thread)) {
LightOrRealThreadInfo filter = getRequestsManager().getFilterThread();
if (filter != null && !filter.checkSameThread(thread, suspendContext)) {
notifySkippedBreakpoints(event, SkippedBreakpointReason.STEPPING);
suspendManager.voteResume(suspendContext);
return;

View File

@@ -879,7 +879,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
DebugProcessImpl debugProcess = context.getDebugProcess();
if (resetThreadFilter) {
BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(debugProcess.getProject()).getBreakpointManager();
breakpointManager.applyThreadFilter(debugProcess, null); // clear the filter on resume
breakpointManager.removeThreadFilter(debugProcess); // clear the filter on resume
}
breakpoint.setSuspendPolicy(
context.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL);
@@ -1025,7 +1025,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
public void dispose() {
Disposer.dispose(myDisposable);
myRequestManager.setFilterThread(null);
myRequestManager.setThreadFilter(null);
}
@Override
@@ -1681,7 +1681,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
showStatusText(JavaDebuggerBundle.message("status.step.out"));
final ThreadReferenceProxyImpl thread = getContextThread();
RequestHint hint = getHint(suspendContext, thread, null);
applyThreadFilter(thread);
applyThreadFilter(getThreadFilterFromContext(suspendContext));
startWatchingMethodReturn(thread);
step(suspendContext, thread, hint, createCommandToken());
super.contextAction(suspendContext);
@@ -1731,7 +1731,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
mySession.setIgnoreStepFiltersFlag(getFrameCount(stepThread, suspendContext));
}
hint.setIgnoreFilters(myForcedIgnoreFilters || mySession.shouldIgnoreSteppingFilters());
applyThreadFilter(stepThread);
applyThreadFilter(getThreadFilterFromContext(suspendContext));
if (myBreakpoint != null) {
prepareAndSetSteppingBreakpoint(suspendContext, myBreakpoint, hint, false);
}
@@ -1813,7 +1813,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
RequestHint hint = getHint(suspendContext, stepThread, null);
applyThreadFilter(stepThread);
applyThreadFilter(getThreadFilterFromContext(suspendContext));
startWatchingMethodReturn(stepThread);
@@ -1852,7 +1852,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
if (myIgnoreBreakpoints) {
DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this);
}
applyThreadFilter(getContextThread());
applyThreadFilter(getThreadFilterFromContext(context));
prepareAndSetSteppingBreakpoint(context, myRunToCursorBreakpoint, null, false);
final DebugProcessImpl debugProcess = context.getDebugProcess();
@@ -1944,14 +1944,19 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
return myContextThread;
}
protected void applyThreadFilter(ThreadReferenceProxy thread) {
@Nullable
public LightOrRealThreadInfo getThreadFilterFromContext(@NotNull SuspendContextImpl suspendContext) {
return myContextThread != null ? new RealThreadInfo(myContextThread.getThreadReference()) : null;
}
protected void applyThreadFilter(@Nullable LightOrRealThreadInfo threadInfo) {
if (getSuspendContext().getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
// there could be explicit resume as a result of call to voteSuspend()
// e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_
// resuming and all breakpoints in other threads will be ignored.
// As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens
final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
breakpointManager.applyThreadFilter(DebugProcessImpl.this, thread.getThreadReference());
breakpointManager.applyThreadFilter(DebugProcessImpl.this, threadInfo);
}
}
}
@@ -2360,7 +2365,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
return new ResumeCommand(suspendContext) {
@Override
public void contextAction(@NotNull SuspendContextImpl suspendContext) {
breakpointManager.applyThreadFilter(DebugProcessImpl.this, null); // clear the filter on resume
breakpointManager.removeThreadFilter(DebugProcessImpl.this); // clear the filter on resume
if (myReturnValueWatcher != null) {
myReturnValueWatcher.clear();
}

View File

@@ -0,0 +1,16 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.debugger.engine
import com.sun.jdi.ThreadReference
interface LightOrRealThreadInfo {
val realThread: ThreadReference?
fun checkSameThread(thread: ThreadReference, suspendContext: SuspendContextImpl): Boolean
}
data class RealThreadInfo(override val realThread: ThreadReference) : LightOrRealThreadInfo {
override fun checkSameThread(thread: ThreadReference, suspendContext: SuspendContextImpl): Boolean {
return realThread == thread
}
}

View File

@@ -47,7 +47,7 @@ public class RequestManagerImpl extends DebugProcessAdapterImpl implements Reque
* It specifies the thread performing suspend-all stepping.
* All events in other threads are ignored.
*/
private @Nullable ThreadReference myFilterThread;
private @Nullable LightOrRealThreadInfo myFilterThread;
public RequestManagerImpl(DebugProcessImpl debugProcess) {
myDebugProcess = debugProcess;
@@ -59,12 +59,27 @@ public class RequestManagerImpl extends DebugProcessAdapterImpl implements Reque
}
@Nullable
public ThreadReference getFilterThread() {
public LightOrRealThreadInfo getFilterThread() {
return myFilterThread;
}
@Nullable
public ThreadReference getFilterRealThread() {
return myFilterThread != null ? myFilterThread.getRealThread() : null;
}
/** @deprecated Use setThreadFilter instead */
@Deprecated
public void setFilterThread(@Nullable final ThreadReference filterThread) {
myFilterThread = filterThread;
if (filterThread != null) {
setThreadFilter(new RealThreadInfo(filterThread));
}
else {
setThreadFilter(null);
}
}
public void setThreadFilter(@Nullable final LightOrRealThreadInfo filter) {
myFilterThread = filter;
}
public Set<EventRequest> findRequests(Requestor requestor) {
@@ -352,7 +367,7 @@ public class RequestManagerImpl extends DebugProcessAdapterImpl implements Reque
DebuggerManagerThreadImpl.assertIsManagerThread();
LOG.assertTrue(findRequestor(request) != null);
try {
final ThreadReference filterThread = myFilterThread;
final ThreadReference filterThread = myFilterThread == null ? null : myFilterThread.getRealThread();
if (filterThread != null && DebuggerSession.filterBreakpointsDuringSteppingUsingDebuggerEngine()) {
if (request instanceof BreakpointRequest) {
((BreakpointRequest)request).addThreadFilter(filterThread);

View File

@@ -708,8 +708,8 @@ public final class DebuggerSession implements AbstractDebuggerSession {
clearSteppingThrough();
}
DebugProcessImpl debugProcess = (DebugProcessImpl)proc;
if (debugProcess.getRequestsManager().getFilterThread() == thread) {
DebuggerManagerEx.getInstanceEx(proc.getProject()).getBreakpointManager().applyThreadFilter(debugProcess, null);
if (debugProcess.getRequestsManager().getFilterRealThread() == thread) {
DebuggerManagerEx.getInstanceEx(proc.getProject()).getBreakpointManager().removeThreadFilter(debugProcess);
}
}

View File

@@ -10,6 +10,8 @@ import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.JavaDebuggerBundle;
import com.intellij.debugger.engine.BreakpointStepMethodFilter;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.LightOrRealThreadInfo;
import com.intellij.debugger.engine.RealThreadInfo;
import com.intellij.debugger.engine.requests.RequestManagerImpl;
import com.intellij.debugger.impl.*;
import com.intellij.openapi.application.ApplicationManager;
@@ -560,19 +562,36 @@ public class BreakpointManager {
}
}
/** @deprecated Use removeThreadFilter or version with LightOrRealThreadInfo parameter */
@Deprecated
public void applyThreadFilter(@NotNull final DebugProcessImpl debugProcess, @Nullable ThreadReference newFilterThread) {
if (newFilterThread != null) {
applyThreadFilter(debugProcess, new RealThreadInfo(newFilterThread));
}
else {
removeThreadFilter(debugProcess);
}
}
public void removeThreadFilter(@NotNull final DebugProcessImpl debugProcess) {
applyThreadFilter(debugProcess, (LightOrRealThreadInfo)null);
}
public void applyThreadFilter(@NotNull final DebugProcessImpl debugProcess, @Nullable LightOrRealThreadInfo filter) {
final RequestManagerImpl requestManager = debugProcess.getRequestsManager();
final ThreadReference oldFilterThread = requestManager.getFilterThread();
if (Comparing.equal(newFilterThread, oldFilterThread)) {
if (Comparing.equal(filter, requestManager.getFilterThread())) {
// the filter already added
return;
}
requestManager.setFilterThread(newFilterThread);
requestManager.setThreadFilter(filter);
if (!DebuggerSession.filterBreakpointsDuringSteppingUsingDebuggerEngine()) {
return;
}
final ThreadReference newFilterThread = filter == null ? null : filter.getRealThread();
final ThreadReference oldFilterThread = requestManager.getFilterRealThread();
EventRequestManager eventRequestManager = requestManager.getVMRequestManager();
if (DebuggerUtilsAsync.isAsyncEnabled() && eventRequestManager instanceof EventRequestManagerImpl) {
Stream<EventRequestManagerImpl.ThreadVisibleEventRequestImpl> requests =

View File

@@ -41,6 +41,6 @@ abstract class AbstractCoroutineBreakpointFacility {
// TODO this is nasty. Find a way to apply an empty thread filter only to the newly created breakpoint
// TODO consider moving this filtering to event loop?
val breakpointManager = DebuggerManagerEx.getInstanceEx(debugProcess.project).breakpointManager
breakpointManager.applyThreadFilter(debugProcess, null)
breakpointManager.removeThreadFilter(debugProcess)
}
}