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 @@ -34,6 +34,7 @@
import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler;
import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler;
import com.microsoft.java.debug.core.adapter.handler.ProcessIdHandler;
import com.microsoft.java.debug.core.adapter.handler.RefreshFramesHandler;
import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler;
import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler;
import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler;
Expand Down Expand Up @@ -133,6 +134,7 @@ private void initialize() {
registerHandlerForDebug(new SetFunctionBreakpointsRequestHandler());
registerHandlerForDebug(new BreakpointLocationsRequestHander());
registerHandlerForDebug(new StepInTargetsRequestHandler());
registerHandlerForDebug(new RefreshFramesHandler());

// NO_DEBUG mode only
registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -32,6 +33,8 @@ protected boolean removeEldestEntry(java.util.Map.Entry<Long, Boolean> eldest) {
}
});
private Map<Long, ThreadReference> eventThreads = new ConcurrentHashMap<>();
private Map<Long, Set<String>> decompiledClassesByThread = new HashMap<>();
private Map<Long, String> threadStoppedReasons = new HashMap<>();

public synchronized void resetThreads(List<ThreadReference> threads) {
allThreads.clear();
Expand Down Expand Up @@ -80,6 +83,13 @@ public void addEventThread(ThreadReference thread) {
eventThreads.put(thread.uniqueID(), thread);
}

public void addEventThread(ThreadReference thread, String reason) {
eventThreads.put(thread.uniqueID(), thread);
if (reason != null) {
threadStoppedReasons.put(thread.uniqueID(), reason);
}
}

public void removeEventThread(long threadId) {
eventThreads.remove(threadId);
}
Expand Down Expand Up @@ -113,4 +123,40 @@ public List<ThreadReference> visibleThreads(IDebugAdapterContext context) {

return visibleThreads;
}

public Set<String> getDecompiledClassesByThread(long threadId) {
return decompiledClassesByThread.get(threadId);
}

public void setDecompiledClassesByThread(long threadId, Set<String> decompiledClasses) {
if (decompiledClasses == null || decompiledClasses.isEmpty()) {
decompiledClassesByThread.remove(threadId);
return;
}

decompiledClassesByThread.put(threadId, decompiledClasses);
}

public String getThreadStoppedReason(long threadId) {
return threadStoppedReasons.get(threadId);
}

public void setThreadStoppedReason(long threadId, String reason) {
if (reason == null) {
threadStoppedReasons.remove(threadId);
return;
}

threadStoppedReasons.put(threadId, reason);
}

public void clearThreadStoppedState(long threadId) {
threadStoppedReasons.remove(threadId);
decompiledClassesByThread.remove(threadId);
}

public void clearAllThreadStoppedState() {
threadStoppedReasons.clear();
decompiledClassesByThread.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
if (context.isVmStopOnEntry()) {
DebugUtility.stopOnEntry(debugSession, context.getMainClass()).thenAccept(threadId -> {
context.getProtocolServer().sendEvent(new Events.StoppedEvent("entry", threadId));
context.getThreadCache().setThreadStoppedReason(threadId, "entry");
});
}
} else if (event instanceof VMDeathEvent) {
Expand Down Expand Up @@ -117,7 +118,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
JdiExceptionReference jdiException = new JdiExceptionReference(((ExceptionEvent) event).exception(),
((ExceptionEvent) event).catchLocation() == null);
context.getExceptionManager().setException(thread.uniqueID(), jdiException);
context.getThreadCache().addEventThread(thread);
context.getThreadCache().addEventThread(thread, "exception");
context.getProtocolServer().sendEvent(new Events.StoppedEvent("exception", thread.uniqueID()));
debugEvent.shouldResume = false;
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*******************************************************************************
* Copyright (c) 2023 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package com.microsoft.java.debug.core.adapter.handler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;

import com.microsoft.java.debug.core.AsyncJdwpUtils;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
import com.microsoft.java.debug.core.protocol.Events.StoppedEvent;
import com.microsoft.java.debug.core.protocol.Messages.Response;
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
import com.microsoft.java.debug.core.protocol.Requests.Command;
import com.microsoft.java.debug.core.protocol.Requests.RefreshFramesArguments;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ThreadReference;

public class RefreshFramesHandler implements IDebugRequestHandler {

@Override
public List<Command> getTargetCommands() {
return Arrays.asList(Command.REFRESHFRAMES);
}

@Override
public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response,
IDebugAdapterContext context) {
RefreshFramesArguments refreshArgs = (RefreshFramesArguments) arguments;
String[] affectedRootPaths = refreshArgs == null ? null : refreshArgs.affectedRootPaths;
List<Long> pausedThreads = getPausedThreads(context);
for (long threadId : pausedThreads) {
if (affectedRootPaths == null || affectedRootPaths.length == 0) {
refreshFrames(threadId, context);
continue;
}

Set<String> decompiledClasses = context.getThreadCache().getDecompiledClassesByThread(threadId);
if (decompiledClasses == null || decompiledClasses.isEmpty()) {
continue;
}

if (anyInAffectedRootPaths(decompiledClasses, affectedRootPaths)) {
refreshFrames(threadId, context);
}
}

return CompletableFuture.completedFuture(response);
}

List<Long> getPausedThreads(IDebugAdapterContext context) {
List<Long> results = new ArrayList<>();
List<CompletableFuture<Long>> futures = new ArrayList<>();
List<ThreadReference> threads = context.getThreadCache().visibleThreads(context);
for (ThreadReference thread : threads) {
if (context.asyncJDWP()) {
futures.add(AsyncJdwpUtils.supplyAsync(() -> {
try {
if (thread.isSuspended()) {
return thread.uniqueID();
}
} catch (ObjectCollectedException ex) {
// Ignore it if the thread is garbage collected.
}

return -1L;
}));
} else {
try {
if (thread.isSuspended()) {
results.add(thread.uniqueID());
}
} catch (ObjectCollectedException ex) {
// Ignore it if the thread is garbage collected.
}
}
}

List<Long> awaitedResutls = AsyncJdwpUtils.await(futures);
for (Long threadId : awaitedResutls) {
if (threadId > 0) {
results.add(threadId);
}
}

return results;
}

/**
* See https://github.com/microsoft/vscode/issues/188606,
* VS Code doesn't provide a simple way to refetch the stack frames.
* We're going to resend a thread stopped event to trick the client
* into refetching the thread stack frames.
*/
void refreshFrames(long threadId, IDebugAdapterContext context) {
StoppedEvent stoppedEvent = new StoppedEvent(context.getThreadCache().getThreadStoppedReason(threadId), threadId);
stoppedEvent.preserveFocusHint = true;
context.getProtocolServer().sendEvent(stoppedEvent);
}

boolean anyInAffectedRootPaths(Collection<String> classes, String[] affectedRootPaths) {
if (affectedRootPaths == null || affectedRootPaths.length == 0) {
return true;
}

for (String classUri : classes) {
// decompiled class uri is like 'jdt://contents/rt.jar/java.io/PrintStream.class?=1.helloworld/%5C/usr%5C/lib%5C/jvm%5C/
// java-8-oracle%5C/jre%5C/lib%5C/rt.jar%3Cjava.io(PrintStream.class'.
if (classUri.startsWith("jdt://contents/")) {
String jarName = classUri.substring("jdt://contents/".length());
int sep = jarName.indexOf("/");
jarName = sep >= 0 ? jarName.substring(0, sep) : jarName;
for (String affectedRootPath : affectedRootPaths) {
if (affectedRootPath.endsWith("/" + jarName) || affectedRootPath.endsWith("\\" + jarName)) {
return true;
}
}
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ private void stepInto(IDebugAdapterContext context, ThreadReference thread) {
// Have to send two events to keep the UI sync with the step in operations:
context.getProtocolServer().sendEvent(new Events.ContinuedEvent(thread.uniqueID()));
context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID()));
context.getThreadCache().setThreadStoppedReason(thread.uniqueID(), "restartframe");
});
request.enable();
thread.resume();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) {
if (resume) {
debugEvent.eventSet.resume();
} else {
context.getThreadCache().addEventThread(bpThread);
context.getThreadCache().addEventThread(bpThread, breakpointName);
context.getProtocolServer().sendEvent(new Events.StoppedEvent(
breakpointName, bpThread.uniqueID()));
}
});
});
} else {
context.getThreadCache().addEventThread(bpThread);
context.getThreadCache().addEventThread(bpThread, breakpointName);
context.getProtocolServer().sendEvent(new Events.StoppedEvent(
breakpointName, bpThread.uniqueID()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ private void registerWatchpointHandler(IDebugAdapterContext context) {
if (resume) {
debugEvent.eventSet.resume();
} else {
context.getThreadCache().addEventThread(bpThread);
context.getThreadCache().addEventThread(bpThread, "data breakpoint");
context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID()));
}
});
});
} else {
context.getThreadCache().addEventThread(bpThread);
context.getThreadCache().addEventThread(bpThread, "data breakpoint");
context.getProtocolServer().sendEvent(new Events.StoppedEvent("data breakpoint", bpThread.uniqueID()));
}
debugEvent.shouldResume = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,15 @@ private void registerMethodBreakpointHandler(IDebugAdapterContext context) {
if (resume) {
debugEvent.eventSet.resume();
} else {
context.getThreadCache().addEventThread(bpThread);
context.getThreadCache().addEventThread(bpThread, "function breakpoint");
context.getProtocolServer().sendEvent(new Events.StoppedEvent(
"function breakpoint", bpThread.uniqueID()));
}
});
});

} else {
context.getThreadCache().addEventThread(bpThread);
context.getThreadCache().addEventThread(bpThread, "function breakpoint");
context.getProtocolServer()
.sendEvent(new Events.StoppedEvent("function breakpoint", bpThread.uniqueID()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
Expand Down Expand Up @@ -73,16 +75,23 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
response.body = new Responses.StackTraceResponseBody(result, 0);
return CompletableFuture.completedFuture(response);
}
ThreadReference thread = context.getThreadCache().getThread(stacktraceArgs.threadId);
long threadId = stacktraceArgs.threadId;
ThreadReference thread = context.getThreadCache().getThread(threadId);
if (thread == null) {
thread = DebugUtility.getThread(context.getDebugSession(), stacktraceArgs.threadId);
thread = DebugUtility.getThread(context.getDebugSession(), threadId);
}
int totalFrames = 0;
if (thread != null) {
Set<String> decompiledClasses = new LinkedHashSet<>();
try {
// Thread state has changed and then invalidate the stack frame cache.
if (stacktraceArgs.startFrame == 0) {
context.getStackFrameManager().clearStackFrames(thread);
} else {
Set<String> existing = context.getThreadCache().getDecompiledClassesByThread(threadId);
if (existing != null) {
decompiledClasses.addAll(existing);
}
}

totalFrames = thread.frameCount();
Expand All @@ -102,6 +111,10 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
Types.StackFrame lspFrame = convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context);
result.add(lspFrame);
frameReference.setSource(lspFrame.source);
int jdiLineNumber = AdapterUtils.convertLineNumber(jdiFrame.lineNumber, context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1());
if (jdiLineNumber != lspFrame.line) {
decompiledClasses.add(lspFrame.source.path);
}
}
} catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException
| AbsentInformationException | ObjectCollectedException
Expand All @@ -110,6 +123,8 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
// 1. the vscode has wrong parameter/wrong uri
// 2. the thread actually terminates
// TODO: should record a error log here.
} finally {
context.getThreadCache().setDecompiledClassesByThread(threadId, decompiledClasses);
}
}
response.body = new Responses.StackTraceResponseBody(result, totalFrames);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
if (threadState.eventSubscription != null) {
threadState.eventSubscription.dispose();
}
context.getThreadCache().addEventThread(thread);
context.getThreadCache().addEventThread(thread, "step");
context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID()));
debugEvent.shouldResume = false;
} else if (event instanceof MethodExitEvent) {
Expand Down
Loading