Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.microsoft.java.debug.core.adapter.ErrorCode;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
import com.microsoft.java.debug.core.protocol.Events;
import com.microsoft.java.debug.core.protocol.Messages.Response;
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
Expand All @@ -32,7 +31,6 @@
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.ExceptionEvent;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.event.ThreadDeathEvent;
import com.sun.jdi.event.ThreadStartEvent;
import com.sun.jdi.event.VMDeathEvent;
Expand Down Expand Up @@ -93,18 +91,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
Events.ThreadEvent threadDeathEvent = new Events.ThreadEvent("exited", deathThread.uniqueID());
context.getProtocolServer().sendEvent(threadDeathEvent);
} else if (event instanceof BreakpointEvent) {
if (debugEvent.eventSet.size() > 1 && debugEvent.eventSet.stream().anyMatch(t -> t instanceof StepEvent)) {
// The StepEvent and BreakpointEvent are grouped in the same event set only if they occurs at the same location and in the same thread.
// In order to avoid two duplicated StoppedEvents, the debugger will skip the BreakpointEvent.
} else {
ThreadReference bpThread = ((BreakpointEvent) event).thread();
IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class);
if (engine.isInEvaluation(bpThread)) {
return;
}
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
debugEvent.shouldResume = false;
}
// ignore since SetBreakpointsRequestHandler has already handled
} else if (event instanceof ExceptionEvent) {
ThreadReference thread = ((ExceptionEvent) event).thread();
context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public CompletableFuture<Messages.Response> handle(Requests.Command command, Req
Types.Capabilities caps = new Types.Capabilities();
caps.supportsConfigurationDoneRequest = true;
caps.supportsHitConditionalBreakpoints = true;
caps.supportsConditionalBreakpoints = true;
caps.supportsSetVariable = true;
caps.supportTerminateDebuggee = true;
Types.ExceptionBreakpointFilter[] exceptionFilters = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -24,12 +25,14 @@
import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugException;
import com.microsoft.java.debug.core.IBreakpoint;
import com.microsoft.java.debug.core.IDebugSession;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.BreakpointManager;
import com.microsoft.java.debug.core.adapter.ErrorCode;
import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent.EventType;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider;
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;
import com.microsoft.java.debug.core.protocol.Events;
Expand All @@ -39,13 +42,21 @@
import com.microsoft.java.debug.core.protocol.Requests.SetBreakpointArguments;
import com.microsoft.java.debug.core.protocol.Responses;
import com.microsoft.java.debug.core.protocol.Types;
import com.sun.jdi.PrimitiveValue;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.StepEvent;

public class SetBreakpointsRequestHandler implements IDebugRequestHandler {

private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);

private BreakpointManager manager = new BreakpointManager();

private boolean registered = false;

@Override
public List<Command> getTargetCommands() {
return Arrays.asList(Command.SETBREAKPOINTS);
Expand Down Expand Up @@ -73,6 +84,11 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Empty debug session.");
}

if (!registered) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should synchronize the register behavior.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should register behavior happen on the initialization?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on initialization, the debug session is not ready, always null in initialize method

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a initialize member of IDebugRequestHandler to handle the initialization. This logic should be there.

registered = true;
registerBreakpointHandler(context);
}

SetBreakpointArguments bpArguments = (SetBreakpointArguments) arguments;
String clientPath = bpArguments.source.path;
if (AdapterUtils.isWindows()) {
Expand Down Expand Up @@ -130,6 +146,55 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
}
}

private void registerBreakpointHandler(IDebugAdapterContext context) {
IDebugSession debugSession = context.getDebugSession();
if (debugSession != null) {
debugSession.getEventHub().events().subscribe(debugEvent -> {
if (!(debugEvent.event instanceof BreakpointEvent)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use observable.filter to only process BreakpointEvent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in PR2

return;
}
Event event = debugEvent.event;
if (debugEvent.eventSet.size() > 1 && debugEvent.eventSet.stream().anyMatch(t -> t instanceof StepEvent)) {
// The StepEvent and BreakpointEvent are grouped in the same event set only if they occurs at the same location and in the same thread.
// In order to avoid two duplicated StoppedEvents, the debugger will skip the BreakpointEvent.
} else {
ThreadReference bpThread = ((BreakpointEvent) event).thread();
IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class);
if (engine.isInEvaluation(bpThread)) {
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If early return, what is the expected value of debugEvent.shouldResume?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldResume is default true, that is expected.

}

// find the breakpoint related to this breakpoint event
IBreakpoint conditionalBP = Arrays.asList(manager.getBreakpoints()).stream().filter(bp -> StringUtils.isNotBlank(bp.getCondition())
&& bp.requests().contains(((BreakpointEvent) event).request())
).findFirst().get();
if (conditionalBP != null) {
CompletableFuture.runAsync(() -> {
Value value;
try {
value = engine.evaluate(conditionalBP.getCondition(), bpThread, 0).get();
if (value instanceof PrimitiveValue) {
boolean evaluationResultAsBool = ((PrimitiveValue) value).booleanValue();
if (!evaluationResultAsBool) {
debugEvent.eventSet.resume();
return;
}
}
} catch (InterruptedException | ExecutionException e) {
// TODO: notify user about evaluation failure
}
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));

});
} else {
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
}
debugEvent.shouldResume = false;
}
});
}
}

private Types.Breakpoint convertDebuggerBreakpointToClient(IBreakpoint breakpoint, IDebugAdapterContext context) {
int id = (int) breakpoint.getProperty("id");
boolean verified = breakpoint.getProperty("verified") != null && (boolean) breakpoint.getProperty("verified");
Expand Down