Skip to content

Commit e1f81a9

Browse files
committed
Added --inspect.SuspensionTimeout option. (GR-54751)
1 parent 3aea4f3 commit e1f81a9

File tree

14 files changed

+420
-70
lines changed

14 files changed

+420
-70
lines changed

docs/tools/chrome-debugger.md

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ information and launch the debugger. Launch debugging via the printed WSS URL di
9292
* `--inspect.WaitAttached=(true|false)` when true, no guest language source code is executed until the inspector client is attached. Unlike `--inspect.Suspend=true`, the execution is resumed right
9393
after the client is attached. This assures that no execution is missed by the
9494
inspector client. It is `false` by default.
95+
* `--inspect.SuspensionTimeout` sets a timeout of a debugger suspension. The debugger session is disconnected after the timeout expires. The timeout value consists of a positive integer value followed by a chronological time unit. For example, '15m' or '10s'. Valid time units are 'ms' for milliseconds, 's' for seconds, 'm' for minutes, 'h' for hours, and 'd' for days. There is no timeout set by default.
9596

9697
### Advanced Debug Options
9798
The following options are for language experts and language developers:

tools/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This changelog summarizes major changes between Truffle Tools versions.
77
* GR-52742: `CPUSamplerData#getDataList()` was introduced and returns all data collected by the sampler as a list of `CPUSamplerData`. For each context on the engine, including the ones that were already collected, there is a corresponding element in the list. `CPUSamplerData#getContextIndex()` returns the index of the data in the list.
88
* GR-53035: Added CPUSampler support for gathering any async stack trace information for each sample, enabled by default. Introduced configuration option `--cpusampler.GatherAsyncStackTrace=true|false`, and corresponding API [`CPUSampler#setGatherAsyncStackTrace`](https://www.graalvm.org/tools/javadoc/com/oracle/truffle/tools/profiler/CPUSampler.html#setGatherAsyncStackTrace(boolean)), to allow disabling async stack trace sampling again.
99
* GR-53035: `StackTraceEntry#isInlined()` is no longer deprecated.
10+
* GR-54751: `--inspect.SuspensionTimeout` option added to set a timeout of a debugger suspension. The debugger session is disconnected after the timeout expires.
1011

1112
## Version 23.0.0
1213
* GR-41407: Added new option `--dap.SourcePath` to allow to resolve sources with relative paths.

tools/src/com.oracle.truffle.tools.chromeinspector.test/src/com/oracle/truffle/tools/chromeinspector/test/InspectorMessageTransportTest.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,24 @@ public void inspectorCloseAfterDisposeTest() {
240240
}
241241
}
242242

243+
@Test
244+
public void inspectorTimeoutTest() {
245+
Session session = new Session(true);
246+
DebuggerEndpoint endpoint = new DebuggerEndpoint("simplePath" + SecureInspectorPathGenerator.getToken(), null);
247+
try (Engine engine = endpoint.onOpen(session, Engine.newBuilder().option("inspect.SuspensionTimeout", "1s"))) {
248+
try (Context context = Context.newBuilder().engine(engine).build()) {
249+
context.eval("sl", "function main() {\n x = 1;\n return x;\n}");
250+
}
251+
}
252+
int numMessages = MESSAGES_TO_BACKEND.length + MESSAGES_TO_CLIENT.length - 2;
253+
Assert.assertEquals(session.messages.toString(), numMessages, session.messages.size());
254+
assertMessages(session.messages, MESSAGES_TO_BACKEND.length - 1, MESSAGES_TO_CLIENT.length - 2);
255+
String timeoutMessage = session.messages.get(numMessages - 1);
256+
String expectedMessage = "2C{\"method\":\"Runtime.consoleAPICalled\",\"params\":{\"args\":[{\"type\":\"string\"," +
257+
"\"value\":\"Timeout of 1000ms as specified via '--inspect.SuspensionTimeout' was reached. The debugger session is disconnected.\"}]";
258+
Assert.assertTrue(timeoutMessage, timeoutMessage.startsWith(expectedMessage));
259+
}
260+
243261
@Test
244262
public void inspectorContextClosedTest() throws IOException, InterruptedException {
245263
Session session = new Session(null);
@@ -303,6 +321,11 @@ private static final class Session {
303321
this.rc = rc;
304322
}
305323

324+
Session(boolean skipResume) {
325+
remote.setSkipResume(skipResume);
326+
this.rc = null;
327+
}
328+
306329
BasicRemote getBasicRemote() {
307330
return remote;
308331
}
@@ -341,11 +364,16 @@ private static final class BasicRemote {
341364
Session.MsgHandler handler;
342365
private final List<String> messages;
343366
private boolean didStep = false;
367+
private boolean skipResume;
344368

345369
BasicRemote(List<String> messages) {
346370
this.messages = messages;
347371
}
348372

373+
private void setSkipResume(boolean skipResume) {
374+
this.skipResume = skipResume;
375+
}
376+
349377
void sendText(String text) throws IOException {
350378
if (!text.startsWith("{\"method\":\"Debugger.scriptParsed\"")) {
351379
synchronized (messages) {
@@ -357,7 +385,7 @@ void sendText(String text) throws IOException {
357385
if (!didStep) {
358386
handler.onMessage("{\"id\":50,\"method\":\"Debugger.stepOver\"}");
359387
didStep = true;
360-
} else {
388+
} else if (!skipResume) {
361389
handler.onMessage("{\"id\":100,\"method\":\"Debugger.resume\"}");
362390
didStep = false;
363391
}
@@ -383,8 +411,12 @@ void setOpenCountLimit(int openCountLimit) {
383411
}
384412

385413
public Engine onOpen(final Session session) {
414+
return onOpen(session, Engine.newBuilder());
415+
}
416+
417+
public Engine onOpen(final Session session, Engine.Builder engineBuilder) {
386418
assert this != null;
387-
Engine.Builder engineBuilder = Engine.newBuilder().serverTransport(new EndpointMessageTransport(session)).option("inspect", PORT);
419+
engineBuilder.serverTransport(new EndpointMessageTransport(session)).option("inspect", PORT);
388420
if (path != null) {
389421
engineBuilder.option("inspect.Path", path);
390422
}

tools/src/com.oracle.truffle.tools.chromeinspector.test/src/com/oracle/truffle/tools/chromeinspector/test/InspectorTestInstrument.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public final class InspectorTestInstrument extends TruffleInstrument {
4848
protected void onCreate(final Env env) {
4949
env.registerService(new InspectSessionInfoProvider() {
5050
@Override
51-
public InspectSessionInfo getSessionInfo(final boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, final List<URI> sourcePath) {
51+
public InspectSessionInfo getSessionInfo(final boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, final List<URI> sourcePath, Long suspensionTimeout) {
5252
return new InspectSessionInfo() {
5353

5454
private InspectServerSession iss;
@@ -57,9 +57,10 @@ public InspectSessionInfo getSessionInfo(final boolean suspend, final boolean in
5757
private long id;
5858

5959
InspectSessionInfo init() {
60-
this.context = new InspectorExecutionContext("test", inspectInternal, inspectInitialization, env, sourcePath, new PrintWriter(env.err(), true));
60+
PrintWriter err = new PrintWriter(env.err(), true);
61+
this.context = new InspectorExecutionContext("test", inspectInternal, inspectInitialization, env, sourcePath, err, err, suspensionTimeout);
6162
this.connectionWatcher = new ConnectionWatcher();
62-
this.iss = InspectServerSession.create(context, suspend, connectionWatcher);
63+
this.iss = InspectServerSession.create(context, suspend, connectionWatcher, () -> this.iss.dispose());
6364
lastServerSession = new WeakReference<>(iss);
6465
this.id = context.getId();
6566
// Fake connection open
@@ -104,7 +105,7 @@ protected void onFinalize(Env env) {
104105
}
105106

106107
interface InspectSessionInfoProvider {
107-
InspectSessionInfo getSessionInfo(boolean suspend, boolean inspectInternal, boolean inspectInitialization, List<URI> sourcePath);
108+
InspectSessionInfo getSessionInfo(boolean suspend, boolean inspectInternal, boolean inspectInitialization, List<URI> sourcePath, Long suspensionTimeout);
108109
}
109110

110111
interface InspectSessionInfo {

tools/src/com.oracle.truffle.tools.chromeinspector.test/src/com/oracle/truffle/tools/chromeinspector/test/InspectorTester.java

+82-9
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,16 @@ public static InspectorTester start(boolean suspend) throws InterruptedException
6666
}
6767

6868
public static InspectorTester start(boolean suspend, final boolean inspectInternal, final boolean inspectInitialization) throws InterruptedException {
69-
return start(suspend, inspectInternal, inspectInitialization, Collections.emptyList());
69+
return start(new Options(suspend, inspectInternal, inspectInitialization));
7070
}
7171

72-
public static InspectorTester start(boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, List<URI> sourcePath) throws InterruptedException {
73-
return start(suspend, inspectInternal, inspectInitialization, sourcePath, null);
74-
}
75-
76-
public static InspectorTester start(boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, List<URI> sourcePath, Consumer<Context> prolog)
72+
public static InspectorTester start(Options options)
7773
throws InterruptedException {
7874
RemoteObject.resetIDs();
7975
ExceptionDetails.resetIDs();
8076
InspectorExecutionContext.resetIDs();
81-
InspectExecThread exec = new InspectExecThread(suspend, inspectInternal, inspectInitialization, sourcePath, prolog);
77+
InspectExecThread exec = new InspectExecThread(options.isSuspend(), options.isInspectInternal(), options.isInspectInitialization(), options.getSourcePath(), options.getProlog(),
78+
options.getSuspensionTimeout());
8279
exec.start();
8380
exec.initialized.acquire();
8481
return new InspectorTester(exec);
@@ -263,13 +260,88 @@ public String receiveMessages(boolean ignoreNotMatched, String... messageParts)
263260
return allMessages.toString();
264261
}
265262

263+
public static final class Options {
264+
265+
private boolean suspend;
266+
private boolean inspectInternal;
267+
private boolean inspectInitialization;
268+
private List<URI> sourcePath = Collections.emptyList();
269+
private Consumer<Context> prolog;
270+
private Long suspensionTimeout;
271+
272+
public Options(boolean suspend) {
273+
this.suspend = suspend;
274+
}
275+
276+
public Options(boolean suspend, final boolean inspectInternal, final boolean inspectInitialization) {
277+
this.suspend = suspend;
278+
this.inspectInternal = inspectInternal;
279+
this.inspectInitialization = inspectInitialization;
280+
}
281+
282+
public boolean isSuspend() {
283+
return suspend;
284+
}
285+
286+
public Options setSuspend(boolean suspend) {
287+
this.suspend = suspend;
288+
return this;
289+
}
290+
291+
public boolean isInspectInternal() {
292+
return inspectInternal;
293+
}
294+
295+
public Options setInspectInternal(boolean inspectInternal) {
296+
this.inspectInternal = inspectInternal;
297+
return this;
298+
}
299+
300+
public boolean isInspectInitialization() {
301+
return inspectInitialization;
302+
}
303+
304+
public Options setInspectInitialization(boolean inspectInitialization) {
305+
this.inspectInitialization = inspectInitialization;
306+
return this;
307+
}
308+
309+
public List<URI> getSourcePath() {
310+
return sourcePath;
311+
}
312+
313+
public Options setSourcePath(List<URI> sourcePath) {
314+
this.sourcePath = sourcePath;
315+
return this;
316+
}
317+
318+
public Consumer<Context> getProlog() {
319+
return prolog;
320+
}
321+
322+
public Options setProlog(Consumer<Context> prolog) {
323+
this.prolog = prolog;
324+
return this;
325+
}
326+
327+
public Long getSuspensionTimeout() {
328+
return suspensionTimeout;
329+
}
330+
331+
public Options setSuspensionTimeout(Long suspensionTimeout) {
332+
this.suspensionTimeout = suspensionTimeout;
333+
return this;
334+
}
335+
}
336+
266337
private static class InspectExecThread extends Thread implements MessageEndpoint {
267338

268339
private final boolean suspend;
269340
private final boolean inspectInternal;
270341
private final boolean inspectInitialization;
271342
private final List<URI> sourcePath;
272343
private final Consumer<Context> prolog;
344+
private final Long suspensionTimeout;
273345
private InspectServerSession inspect;
274346
private ConnectionWatcher connectionWatcher;
275347
private long contextId;
@@ -284,13 +356,14 @@ private static class InspectExecThread extends Thread implements MessageEndpoint
284356
final ProxyOutputStream err = new ProxyOutputStream(System.err);
285357
private final EnginesGCedTest.GCCheck gcCheck;
286358

287-
InspectExecThread(boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, List<URI> sourcePath, Consumer<Context> prolog) {
359+
InspectExecThread(boolean suspend, final boolean inspectInternal, final boolean inspectInitialization, List<URI> sourcePath, Consumer<Context> prolog, Long suspensionTimeout) {
288360
super("Inspector Executor");
289361
this.suspend = suspend;
290362
this.inspectInternal = inspectInternal;
291363
this.inspectInitialization = inspectInitialization;
292364
this.sourcePath = sourcePath;
293365
this.prolog = prolog;
366+
this.suspensionTimeout = suspensionTimeout;
294367
this.gcCheck = new EnginesGCedTest.GCCheck();
295368
}
296369

@@ -304,7 +377,7 @@ public void run() {
304377
}
305378
Instrument testInstrument = engine.getInstruments().get(InspectorTestInstrument.ID);
306379
InspectSessionInfoProvider sessionInfoProvider = testInstrument.lookup(InspectSessionInfoProvider.class);
307-
InspectSessionInfo sessionInfo = sessionInfoProvider.getSessionInfo(suspend, inspectInternal, inspectInitialization, sourcePath);
380+
InspectSessionInfo sessionInfo = sessionInfoProvider.getSessionInfo(suspend, inspectInternal, inspectInitialization, sourcePath, suspensionTimeout);
308381
inspect = sessionInfo.getInspectServerSession();
309382
try {
310383
connectionWatcher = sessionInfo.getConnectionWatcher();

tools/src/com.oracle.truffle.tools.chromeinspector.test/src/com/oracle/truffle/tools/chromeinspector/test/RelativeSourceInspectDebugTest.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public void testSourcePath() throws Exception {
119119
resolvedURI[2] = fs.getPath(folderInZip3, relativePath[2]).toUri();
120120
}
121121

122-
InspectorTester tester = InspectorTester.start(true, false, false, Arrays.asList(sourcePathURI));
122+
InspectorTester tester = InspectorTester.start(new InspectorTester.Options(true, false, false).setSourcePath(Arrays.asList(sourcePathURI)));
123123
tester.sendMessage("{\"id\":1,\"method\":\"Runtime.enable\"}");
124124
assertEquals("{\"result\":{},\"id\":1}", tester.getMessages(true).trim());
125125
tester.sendMessage("{\"id\":2,\"method\":\"Debugger.enable\"}");
@@ -303,7 +303,7 @@ private static void testBreakpoints(int sectionLine, int sectionColumn, int bpLi
303303
ProxyLanguage.setDelegate(language);
304304
Source source = Source.create(ProxyLanguage.ID, sourceContent);
305305

306-
InspectorTester tester = InspectorTester.start(false, false, false, Collections.singletonList(sourcePathURI));
306+
InspectorTester tester = InspectorTester.start(new InspectorTester.Options(false, false, false).setSourcePath(Collections.singletonList(sourcePathURI)));
307307
tester.sendMessage("{\"id\":1,\"method\":\"Runtime.enable\"}");
308308
assertEquals("{\"result\":{},\"id\":1}", tester.getMessages(true).trim());
309309
tester.sendMessage("{\"id\":2,\"method\":\"Debugger.enable\"}");
@@ -365,7 +365,7 @@ public void testEagerSourceLoad() throws Exception {
365365
Files.write(filePath, sourceContent.getBytes());
366366
String fileURI = filePath.toUri().toString();
367367

368-
InspectorTester tester = InspectorTester.start(true, false, false, Collections.singletonList(sourcePathURI), (context) -> {
368+
InspectorTester tester = InspectorTester.start(new InspectorTester.Options(true, false, false).setSourcePath(Collections.singletonList(sourcePathURI)).setProlog((context) -> {
369369
TestDebugNoContentLanguage language = new TestDebugNoContentLanguage(relativePathProlog1, true, true);
370370
ProxyLanguage.setDelegate(language);
371371
Source sourceProlog = Source.create(ProxyLanguage.ID, sourceContent);
@@ -375,7 +375,7 @@ public void testEagerSourceLoad() throws Exception {
375375
ProxyLanguage.setDelegate(language);
376376
sourceProlog = Source.create(ProxyLanguage.ID, "relative source2\nVarB");
377377
context.eval(sourceProlog);
378-
});
378+
}));
379379

380380
tester.sendMessage("{\"id\":1,\"method\":\"Runtime.enable\"}");
381381
assertEquals("{\"result\":{},\"id\":1}", tester.getMessages(true).trim());

0 commit comments

Comments
 (0)