Skip to content

Commit 8a1c975

Browse files
customRequest 'processId' to get the debugging Java process id (microsoft#399)
* customRequest 'processId' to get Java process id * send a custom notification when processId/shellProcessId is ready
1 parent f704199 commit 8a1c975

File tree

10 files changed

+162
-6
lines changed

10 files changed

+162
-6
lines changed

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.microsoft.java.debug.core.adapter.handler.InitializeRequestHandler;
3333
import com.microsoft.java.debug.core.adapter.handler.InlineValuesRequestHandler;
3434
import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler;
35+
import com.microsoft.java.debug.core.adapter.handler.ProcessIdHandler;
3536
import com.microsoft.java.debug.core.adapter.handler.RefreshVariablesHandler;
3637
import com.microsoft.java.debug.core.adapter.handler.RestartFrameHandler;
3738
import com.microsoft.java.debug.core.adapter.handler.ScopesRequestHandler;
@@ -125,10 +126,11 @@ private void initialize() {
125126
registerHandlerForDebug(new SetDataBreakpointsRequestHandler());
126127
registerHandlerForDebug(new InlineValuesRequestHandler());
127128
registerHandlerForDebug(new RefreshVariablesHandler());
129+
registerHandlerForDebug(new ProcessIdHandler());
128130

129131
// NO_DEBUG mode only
130132
registerHandlerForNoDebug(new DisconnectRequestWithoutDebuggingHandler());
131-
133+
registerHandlerForNoDebug(new ProcessIdHandler());
132134
}
133135

134136
private void registerHandlerForDebug(IDebugRequestHandler handler) {

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public class DebugAdapterContext implements IDebugAdapterContext {
5454
private Path classpathJar = null;
5555
private Path argsfile = null;
5656

57+
private long shellProcessId = -1;
58+
private long processId = -1;
59+
5760
private IdCollection<String> sourceReferences = new IdCollection<>();
5861
private RecyclableObjectPool<Long, Object> recyclableIdPool = new RecyclableObjectPool<>();
5962
private IVariableFormatter variableFormatter = VariableFormatterFactory.createVariableFormatter();
@@ -326,4 +329,24 @@ public IBreakpointManager getBreakpointManager() {
326329
public IStepResultManager getStepResultManager() {
327330
return stepResultManager;
328331
}
332+
333+
@Override
334+
public long getProcessId() {
335+
return this.processId;
336+
}
337+
338+
@Override
339+
public long getShellProcessId() {
340+
return this.shellProcessId;
341+
}
342+
343+
@Override
344+
public void setProcessId(long processId) {
345+
this.processId = processId;
346+
}
347+
348+
@Override
349+
public void setShellProcessId(long shellProcessId) {
350+
this.shellProcessId = shellProcessId;
351+
}
329352
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,12 @@ public interface IDebugAdapterContext {
128128
IBreakpointManager getBreakpointManager();
129129

130130
IStepResultManager getStepResultManager();
131+
132+
void setShellProcessId(long shellProcessId);
133+
134+
long getShellProcessId();
135+
136+
void setProcessId(long processId);
137+
138+
long getProcessId();
131139
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,17 @@ protected CompletableFuture<Response> handleLaunchCommand(Arguments arguments, R
138138
}
139139

140140
return launch(launchArguments, response, context).thenCompose(res -> {
141+
long processId = context.getProcessId();
142+
long shellProcessId = context.getShellProcessId();
143+
if (context.getDebuggeeProcess() != null) {
144+
processId = context.getDebuggeeProcess().pid();
145+
}
146+
147+
// If processId or shellProcessId exist, send a notification to client.
148+
if (processId > 0 || shellProcessId > 0) {
149+
context.getProtocolServer().sendEvent(new Events.ProcessIdNotification(processId, shellProcessId));
150+
}
151+
141152
LaunchUtils.releaseTempLaunchFile(context.getClasspathJar());
142153
LaunchUtils.releaseTempLaunchFile(context.getArgsfile());
143154
if (res.success) {

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.commons.lang3.SystemUtils;
2525

2626
import com.google.gson.JsonObject;
27+
import com.google.gson.JsonSyntaxException;
2728
import com.microsoft.java.debug.core.Configuration;
2829
import com.microsoft.java.debug.core.DebugException;
2930
import com.microsoft.java.debug.core.DebugSession;
@@ -45,6 +46,7 @@
4546
import com.microsoft.java.debug.core.protocol.Requests.Command;
4647
import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments;
4748
import com.microsoft.java.debug.core.protocol.Requests.RunInTerminalRequestArguments;
49+
import com.microsoft.java.debug.core.protocol.Responses.RunInTerminalResponseBody;
4850
import com.sun.jdi.VirtualMachine;
4951
import com.sun.jdi.connect.Connector;
5052
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
@@ -102,6 +104,14 @@ public CompletableFuture<Response> launchInTerminal(LaunchArguments launchArgume
102104
if (runResponse != null) {
103105
if (runResponse.success) {
104106
try {
107+
try {
108+
RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson(
109+
JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class);
110+
context.setProcessId(terminalResponse.processId);
111+
context.setShellProcessId(terminalResponse.shellProcessId);
112+
} catch (JsonSyntaxException e) {
113+
logger.severe("Failed to resolve runInTerminal response: " + e.toString());
114+
}
105115
VirtualMachine vm = listenConnector.accept(args);
106116
vmHandler.connectVirtualMachine(vm);
107117
context.setDebugSession(new DebugSession(vm));

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithoutDebuggingDelegate.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.logging.Logger;
2222

2323
import com.google.gson.JsonObject;
24+
import com.google.gson.JsonSyntaxException;
2425
import com.microsoft.java.debug.core.Configuration;
2526
import com.microsoft.java.debug.core.DebugException;
2627
import com.microsoft.java.debug.core.adapter.ErrorCode;
@@ -33,6 +34,7 @@
3334
import com.microsoft.java.debug.core.protocol.Requests.Command;
3435
import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments;
3536
import com.microsoft.java.debug.core.protocol.Requests.RunInTerminalRequestArguments;
37+
import com.microsoft.java.debug.core.protocol.Responses.RunInTerminalResponseBody;
3638
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
3739
import com.sun.jdi.connect.VMStartException;
3840

@@ -112,8 +114,19 @@ public CompletableFuture<Response> launchInTerminal(LaunchArguments launchArgume
112114
context.getProtocolServer().sendRequest(request, RUNINTERMINAL_TIMEOUT).whenComplete((runResponse, ex) -> {
113115
if (runResponse != null) {
114116
if (runResponse.success) {
115-
// Without knowing the pid, debugger has lost control of the process.
116-
// So simply send `terminated` event to end the session.
117+
try {
118+
RunInTerminalResponseBody terminalResponse = JsonUtils.fromJson(
119+
JsonUtils.toJson(runResponse.body), RunInTerminalResponseBody.class);
120+
context.setProcessId(terminalResponse.processId);
121+
context.setShellProcessId(terminalResponse.shellProcessId);
122+
} catch (JsonSyntaxException e) {
123+
logger.severe("Failed to resolve runInTerminal response: " + e.toString());
124+
}
125+
126+
// TODO: Since the RunInTerminal request will return the pid or parent shell
127+
// pid now, the debugger is able to use this pid to monitor the lifecycle
128+
// of the running Java process. There is no need to terminate the debug
129+
// session early here.
117130
context.getProtocolServer().sendEvent(new Events.TerminatedEvent());
118131
resultFuture.complete(response);
119132
} else {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter.handler;
13+
14+
import java.util.Arrays;
15+
import java.util.List;
16+
import java.util.concurrent.CompletableFuture;
17+
18+
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
19+
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
20+
import com.microsoft.java.debug.core.protocol.Messages.Response;
21+
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
22+
import com.microsoft.java.debug.core.protocol.Requests.Command;
23+
import com.microsoft.java.debug.core.protocol.Responses.ProcessIdResponseBody;
24+
25+
public class ProcessIdHandler implements IDebugRequestHandler {
26+
@Override
27+
public List<Command> getTargetCommands() {
28+
return Arrays.asList(Command.PROCESSID);
29+
}
30+
31+
@Override
32+
public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response,
33+
IDebugAdapterContext context) {
34+
long processId = context.getProcessId();
35+
long shellProcessId = context.getShellProcessId();
36+
if (context.getDebuggeeProcess() != null) {
37+
processId = context.getDebuggeeProcess().pid();
38+
}
39+
40+
response.body = new ProcessIdResponseBody(processId, shellProcessId);
41+
return CompletableFuture.completedFuture(response);
42+
}
43+
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,4 +283,26 @@ public InvalidatedEvent(InvalidatedAreas area, int frameId) {
283283
this.frameId = frameId;
284284
}
285285
}
286+
287+
public static class ProcessIdNotification extends DebugEvent {
288+
/**
289+
* The process ID.
290+
*/
291+
public long processId = -1;
292+
/**
293+
* The process ID of the terminal shell if the process is running in a terminal shell.
294+
*/
295+
public long shellProcessId = -1;
296+
297+
public ProcessIdNotification(long processId) {
298+
super("processid");
299+
this.processId = processId;
300+
}
301+
302+
public ProcessIdNotification(long processId, long shellProcessId) {
303+
super("processid");
304+
this.processId = processId;
305+
this.shellProcessId = shellProcessId;
306+
}
307+
}
286308
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ public static enum Command {
410410
PAUSEOTHERS("pauseOthers", ThreadOperationArguments.class),
411411
INLINEVALUES("inlineValues", InlineValuesArguments.class),
412412
REFRESHVARIABLES("refreshVariables", RefreshVariablesArguments.class),
413+
PROCESSID("processId", Arguments.class),
413414
UNSUPPORTED("", Arguments.class);
414415

415416
private String command;

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Responses.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,34 @@ public InitializeResponseBody(Types.Capabilities capabilities) {
3939
}
4040
}
4141

42-
public static class RunInTerminalResponseBody extends ResponseBody {
43-
public int processId;
42+
public static class ProcessIdResponseBody extends ResponseBody {
43+
/**
44+
* The process ID.
45+
*/
46+
public long processId = -1;
47+
/**
48+
* The process ID of the terminal shell if the process is running in a terminal shell.
49+
*/
50+
public long shellProcessId = -1;
51+
52+
public ProcessIdResponseBody(long processId) {
53+
this.processId = processId;
54+
}
4455

45-
public RunInTerminalResponseBody(int processId) {
56+
public ProcessIdResponseBody(long processId, long shellProcessId) {
4657
this.processId = processId;
58+
this.shellProcessId = shellProcessId;
59+
}
60+
}
61+
62+
public static class RunInTerminalResponseBody extends ProcessIdResponseBody {
63+
64+
public RunInTerminalResponseBody(long processId) {
65+
super(processId);
66+
}
67+
68+
public RunInTerminalResponseBody(long processId, long shellProcessId) {
69+
super(processId, shellProcessId);
4770
}
4871
}
4972

0 commit comments

Comments
 (0)