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
@@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2019 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;

import com.sun.jdi.ObjectReference;

public class JdiExceptionReference {
public ObjectReference exception;
public boolean isUncaught;

public JdiExceptionReference(ObjectReference exception, boolean isUncaught) {
this.exception = exception;
this.isUncaught = isUncaught;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.microsoft.java.debug.core.adapter.handler.DisconnectRequestHandler;
import com.microsoft.java.debug.core.adapter.handler.DisconnectRequestWithoutDebuggingHandler;
import com.microsoft.java.debug.core.adapter.handler.EvaluateRequestHandler;
import com.microsoft.java.debug.core.adapter.handler.ExceptionInfoRequestHandler;
import com.microsoft.java.debug.core.adapter.handler.HotCodeReplaceHandler;
import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler;
import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler;
Expand Down Expand Up @@ -115,6 +116,7 @@ private void initialize() {
registerHandlerForDebug(new HotCodeReplaceHandler());
registerHandlerForDebug(new RestartFrameHandler());
registerHandlerForDebug(new CompletionsHandler());
registerHandlerForDebug(new ExceptionInfoRequestHandler());

// NO_DEBUG mode only
registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class DebugAdapterContext implements IDebugAdapterContext {
private boolean debuggerLinesStartAt1 = true;
private boolean debuggerPathsAreUri = true;
private boolean clientLinesStartAt1 = true;
private boolean clientColumnsStartAt1 = true;
private boolean clientPathsAreUri = false;
private boolean supportsRunInTerminalRequest;
private boolean isAttached = false;
Expand All @@ -51,6 +52,7 @@ public class DebugAdapterContext implements IDebugAdapterContext {
private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter();

private IStackFrameManager stackFrameManager = new StackFrameManager();
private IExceptionManager exceptionManager = new ExceptionManager();

public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) {
this.providerContext = providerContext;
Expand Down Expand Up @@ -107,6 +109,14 @@ public void setClientLinesStartAt1(boolean clientLinesStartAt1) {
this.clientLinesStartAt1 = clientLinesStartAt1;
}

public boolean isClientColumnsStartAt1() {
return clientColumnsStartAt1;
}

public void setClientColumnsStartAt1(boolean clientColumnsStartAt1) {
this.clientColumnsStartAt1 = clientColumnsStartAt1;
}

@Override
public boolean isClientPathsAreUri() {
return clientPathsAreUri;
Expand Down Expand Up @@ -276,4 +286,9 @@ public void setArgsfile(Path argsfile) {
public Path getArgsfile() {
return this.argsfile;
}

@Override
public IExceptionManager getExceptionManager() {
return this.exceptionManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public enum ErrorCode {
STEP_FAILURE(1015),
RESTARTFRAME_FAILURE(1016),
COMPLETIONS_FAILURE(1017),
EXCEPTION_INFO_FAILURE(1018),
EVALUATION_COMPILE_ERROR(2001),
EVALUATE_NOT_SUSPENDED_THREAD(2002);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2019 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;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import com.microsoft.java.debug.core.JdiExceptionReference;

public class ExceptionManager implements IExceptionManager {
Comment thread
testforstephen marked this conversation as resolved.
Resolved
private Map<Long, JdiExceptionReference> exceptions = Collections.synchronizedMap(new HashMap<>());

@Override
public JdiExceptionReference getException(long threadId) {
return exceptions.get(threadId);
}

@Override
public JdiExceptionReference removeException(long threadId) {
return exceptions.remove(threadId);
}

@Override
public JdiExceptionReference setException(long threadId, JdiExceptionReference exception) {
return exceptions.put(threadId, exception);
}

@Override
public void removeAllExceptions() {
exceptions.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public interface IDebugAdapterContext {

void setClientLinesStartAt1(boolean clientLinesStartAt1);

boolean isClientColumnsStartAt1();

void setClientColumnsStartAt1(boolean clientColumnsStartAt1);

boolean isClientPathsAreUri();

void setClientPathsAreUri(boolean clientPathsAreUri);
Expand Down Expand Up @@ -118,4 +122,6 @@ public interface IDebugAdapterContext {
void setArgsfile(Path argsfile);

Path getArgsfile();

IExceptionManager getExceptionManager();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2019 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;

import com.microsoft.java.debug.core.JdiExceptionReference;

public interface IExceptionManager {
/**
* Returns the Exception associated with the thread.
*/
JdiExceptionReference getException(long threadId);

/**
* Removes the Exception associated with the thread. Returns the previous Exception mapping to the thread,
* null if no mapping exists.
*/
JdiExceptionReference removeException(long threadId);

/**
* Associates an Exception with the thread. Returns the previous Exception mapping to the thread,
* null if no mapping exists before.
*/
JdiExceptionReference setException(long threadId, JdiExceptionReference exception);

/**
* Clear all Exceptions.
*/
void removeAllExceptions();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugEvent;
import com.microsoft.java.debug.core.DebugUtility;
import com.microsoft.java.debug.core.IDebugSession;
import com.microsoft.java.debug.core.JdiExceptionReference;
import com.microsoft.java.debug.core.UsageDataSession;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.ErrorCode;
Expand All @@ -39,6 +42,7 @@
import com.sun.jdi.event.VMStartEvent;

public class ConfigurationDoneRequestHandler implements IDebugRequestHandler {
protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);

@Override
public List<Command> getTargetCommands() {
Expand Down Expand Up @@ -100,6 +104,10 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
if (engine.isInEvaluation(bpThread)) {
return;
}

JdiExceptionReference jdiException = new JdiExceptionReference(((ExceptionEvent) event).exception(),
((ExceptionEvent) event).catchLocation() == null);
context.getExceptionManager().setException(thread.uniqueID(), jdiException);
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,90 @@
/*******************************************************************************
* Copyright (c) 2019 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.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugUtility;
import com.microsoft.java.debug.core.JdiExceptionReference;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
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.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.ExceptionInfoArguments;
import com.microsoft.java.debug.core.protocol.Responses;
import com.microsoft.java.debug.core.protocol.Types.ExceptionBreakMode;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;

public class ExceptionInfoRequestHandler implements IDebugRequestHandler {
protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);

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

@Override
public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response,
IDebugAdapterContext context) {
ExceptionInfoArguments exceptionInfoArgs = (ExceptionInfoArguments) arguments;
ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), exceptionInfoArgs.threadId);
if (thread == null) {
throw AdapterUtils.createCompletionException("Thread " + exceptionInfoArgs.threadId + " doesn't exist.", ErrorCode.EXCEPTION_INFO_FAILURE);
}

JdiExceptionReference jdiException = context.getExceptionManager().getException(exceptionInfoArgs.threadId);
if (jdiException == null) {
throw AdapterUtils.createCompletionException("No exception exists in thread " + exceptionInfoArgs.threadId, ErrorCode.EXCEPTION_INFO_FAILURE);
}

Method toStringMethod = null;
for (Method method : jdiException.exception.referenceType().allMethods()) {
if (Objects.equals("toString", method.name()) && Objects.equals("()Ljava/lang/String;", method.signature())) {
toStringMethod = method;
break;
}
}

String typeName = jdiException.exception.type().name();
String exceptionToString = typeName;
if (toStringMethod != null) {
try {
Value returnValue = jdiException.exception.invokeMethod(thread, toStringMethod, Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);
exceptionToString = returnValue.toString();
} catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException
| InvocationException e) {
logger.log(Level.SEVERE, String.format("Failed to get the return value of the method Exception.toString(): %s", e.toString(), e));
}
}

response.body = new Responses.ExceptionInfoResponse(typeName, exceptionToString,
jdiException.isUncaught ? ExceptionBreakMode.USERUNHANDLED : ExceptionBreakMode.ALWAYS);
return CompletableFuture.completedFuture(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public CompletableFuture<Messages.Response> handle(Requests.Command command, Req
IDebugAdapterContext context) {
Requests.InitializeArguments initializeArguments = (Requests.InitializeArguments) argument;
context.setClientLinesStartAt1(initializeArguments.linesStartAt1);
context.setClientColumnsStartAt1(initializeArguments.columnsStartAt1);
String pathFormat = initializeArguments.pathFormat;
if (pathFormat != null) {
switch (pathFormat) {
Expand Down Expand Up @@ -59,6 +60,7 @@ public CompletableFuture<Messages.Response> handle(Requests.Command command, Req
Types.ExceptionBreakpointFilter.CAUGHT_EXCEPTION_FILTER,
};
caps.exceptionBreakpointFilters = exceptionFilters;
caps.supportsExceptionInfoRequest = true;
response.body = caps;
return CompletableFuture.completedFuture(response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ private Types.StackFrame convertDebuggerStackFrameToClient(StackFrame stackFrame
clientSource = null;
}
}

return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, 0);
return new Types.StackFrame(frameId, methodName, clientSource, lineNumber, context.isClientColumnsStartAt1() ? 1 : 0);
}

private Types.Source convertDebuggerSourceToClient(Location location, IDebugAdapterContext context) throws URISyntaxException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.microsoft.java.debug.core.DebugEvent;
import com.microsoft.java.debug.core.DebugUtility;
import com.microsoft.java.debug.core.IDebugSession;
import com.microsoft.java.debug.core.JdiExceptionReference;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.ErrorCode;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
Expand Down Expand Up @@ -59,6 +60,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
long threadId = ((StepArguments) arguments).threadId;
ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), threadId);
if (thread != null) {
JdiExceptionReference exception = context.getExceptionManager().removeException(threadId);
try {
ThreadState threadState = new ThreadState();
threadState.threadId = threadId;
Expand Down Expand Up @@ -87,12 +89,16 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,

ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context);
} catch (IncompatibleThreadStateException ex) {
// Roll back the Exception info if stepping fails.
context.getExceptionManager().setException(threadId, exception);
final String failureMessage = String.format("Failed to step because the thread '%s' is not suspended in the target VM.", thread.name());
throw AdapterUtils.createCompletionException(
failureMessage,
ErrorCode.STEP_FAILURE,
ex);
} catch (IndexOutOfBoundsException ex) {
// Roll back the Exception info if stepping fails.
context.getExceptionManager().setException(threadId, exception);
final String failureMessage = String.format("Failed to step because the thread '%s' doesn't contain any stack frame", thread.name());
throw AdapterUtils.createCompletionException(
failureMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ private CompletableFuture<Response> resume(Requests.ContinueArguments arguments,
* be resumed (through ThreadReference#resume() or VirtualMachine#resume()) the same number of times it has been suspended.
*/
if (thread != null) {
context.getExceptionManager().removeException(arguments.threadId);
allThreadsContinued = false;
DebugUtility.resumeThread(thread);
checkThreadRunningAndRecycleIds(thread, context);
} else {
context.getExceptionManager().removeAllExceptions();
context.getDebugSession().resume();
context.getRecyclableIdPool().removeAllObjects();
}
Expand Down
Loading