Add Chrome devtools and DAP tools for debugging (#8344)

Adds chrome-inspector tool and Debug Adapter protocol tool for debugging Enso.

# Important Notes
The chrome devtools seems to be broken (tracked in https://github.com/oracle/graal/issues/7636). Even `graalpy --inspect ...` fails (Chrome devtools freezes after connecting to the server). This PR adds Debug adapter protocol tool for debugging as a workaround for chrome inspector. There is docs in [dap.md](https://github.com/enso-org/enso/pull/8344/files#diff-421574b50574cfe546e86d4b3d32d79b8b2087f2fe204f68e5cf2693af43bbe1)
This commit is contained in:
Pavel Marek 2023-11-22 18:18:41 +01:00 committed by GitHub
parent f60836d9e1
commit 268e595ec1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 21 deletions

View File

@ -1428,7 +1428,9 @@ lazy val runtime = (project in file("engine/runtime"))
GraalVM.modules.map(_.withConfigurations(Some(Runtime.name))) GraalVM.modules.map(_.withConfigurations(Some(Runtime.name)))
val langs = val langs =
GraalVM.langsPkgs.map(_.withConfigurations(Some(Runtime.name))) GraalVM.langsPkgs.map(_.withConfigurations(Some(Runtime.name)))
necessaryModules ++ langs val tools =
GraalVM.toolsPkgs.map(_.withConfigurations(Some(Runtime.name)))
necessaryModules ++ langs ++ tools
}, },
Test / javaOptions ++= testLogProviderOptions ++ Seq( Test / javaOptions ++= testLogProviderOptions ++ Seq(
"-Dpolyglotimpl.DisableClassPathIsolation=true" "-Dpolyglotimpl.DisableClassPathIsolation=true"

View File

@ -15,6 +15,8 @@ used by Enso, broken up as follows:
Debugger. Debugger.
- [**Chrome devtools debugger:**](./chrome-devtools.md) A guide how to debug - [**Chrome devtools debugger:**](./chrome-devtools.md) A guide how to debug
Enso code using Chrome devtools. Enso code using Chrome devtools.
- [**Debug adapter protocol:**](./dap.md) A guide how to debug Enso code using
VSCode.
- [**Debugging Enso and Java code at once:**](./mixed-debugging.md) A - [**Debugging Enso and Java code at once:**](./mixed-debugging.md) A
step-by-step guide how to debug both Enso and Java code in a single debugger. step-by-step guide how to debug both Enso and Java code in a single debugger.
- [**Debugging Engine (Runtime) only Java code:**](./runtime-debugging.md) A - [**Debugging Engine (Runtime) only Java code:**](./runtime-debugging.md) A

31
docs/debugger/dap.md Normal file
View File

@ -0,0 +1,31 @@
# Debug Adapter Protocol
[Debug Adapter Protocol](https://www.graalvm.org/latest/tools/dap/) is yet
another instrument available for a Truffle language. The DAP is a native
protocol for VSCode and as such, works only via VSCode. To start Enso with DAP
server waiting for a client to attach, launch enso via:
```
env JAVA_OPTS='-Dpolyglot.dap' ./built-distribution/enso-engine-*/enso-*/bin/enso --run *.enso
```
Once DAP server is started and ready for a client to be attached, the following
output will be printed:
```
[Graal DAP] Starting server and listening on /127.0.0.1:4711
```
There is a
[Launch configuration](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations)
in the repository in
[.vscode/launch.json](https://github.com/enso-org/enso/blob/a123cd0d9f4b04d05aae7a5231efba554062188f/.vscode/launch.json#L13-L16)
with name `Debug Adapter Protocol`, you can start debugging via
[Run and Debug view](https://code.visualstudio.com/docs/editor/debugging#_run-and-debug-view)
by selecting the `Debug adapter protocol` configuration and pressing play:
![image](https://github.com/enso-org/enso/assets/14013887/7f15abfd-b4fa-45d3-a100-142c465b6444)
Another screenshot showing the DAP in action:
![image](https://github.com/enso-org/enso/assets/14013887/41dd8b80-dbac-4a11-b3e2-97c99e42c507)
Note that the port 4711 is the default port for DAP.

View File

@ -11,11 +11,11 @@ import org.enso.polyglot.{
PolyglotContext, PolyglotContext,
RuntimeOptions RuntimeOptions
} }
import org.graalvm.polyglot.Engine import org.graalvm.polyglot.{Context, Engine}
import org.graalvm.polyglot.Context
import org.slf4j.event.Level import org.slf4j.event.Level
import java.io.{File, InputStream, OutputStream} import java.io.{ByteArrayOutputStream, File, InputStream, OutputStream}
import scala.util.{Failure, Success, Using}
/** Utility class for creating Graal polyglot contexts. /** Utility class for creating Graal polyglot contexts.
*/ */
@ -131,14 +131,7 @@ class ContextFactory {
if (graalpy.exists()) { if (graalpy.exists()) {
builder.option("python.Executable", graalpy.getAbsolutePath()); builder.option("python.Executable", graalpy.getAbsolutePath());
} }
if ( if (engineHasJava()) {
Engine
.newBuilder()
.allowExperimentalOptions(true)
.build()
.getLanguages()
.containsKey("java")
) {
builder builder
.option("java.ExposeNativeJavaVM", "true") .option("java.ExposeNativeJavaVM", "true")
.option("java.Polyglot", "true") .option("java.Polyglot", "true")
@ -148,4 +141,24 @@ class ContextFactory {
} }
new PolyglotContext(builder.build) new PolyglotContext(builder.build)
} }
/** Checks whether the polyglot engine has Espresso.
*
* Creates a temporary polyglot engine for that and makes sure that it is closed.
*/
private def engineHasJava(): Boolean = {
Using(
Engine
.newBuilder()
.allowExperimentalOptions(true)
.out(new ByteArrayOutputStream())
.err(new ByteArrayOutputStream())
.build()
) { engine =>
engine.getLanguages.containsKey("java")
} match {
case Success(ret) => ret
case Failure(ex) => throw new IllegalStateException("unreachable", ex)
}
}
} }

View File

@ -457,17 +457,20 @@ public class DebuggingEnsoTest {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Ignore
@Test @Test
public void testSteppingOver() { public void testSteppingOver() {
Source src = createEnsoSource(""" Source src = createEnsoSource("""
baz x = x # 1 baz x = x # 1
bar x = baz x # 2 bar x = # 2
foo x = # 3 ret = baz x # 3
bar 42 # 4 ret # 4
end = 0 # 5 foo x = # 5
bar 42 # 6
end = 0 # 7
"""); """);
List<Integer> expectedLineNumbers = List.of(3, 4, 5); // Steps into line 2 - declaration of the method, which is fine.
// (5, 6, 7) would be better.
List<Integer> expectedLineNumbers = List.of(5, 6, 2, 7);
Queue<SuspendedCallback> steps = createStepOverEvents(expectedLineNumbers.size()); Queue<SuspendedCallback> steps = createStepOverEvents(expectedLineNumbers.size());
testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers);
} }
@ -475,6 +478,10 @@ public class DebuggingEnsoTest {
/** /**
* Use some methods from Vector in stdlib. Stepping over methods from different * Use some methods from Vector in stdlib. Stepping over methods from different
* modules might be problematic. * modules might be problematic.
*
* TODO[pm] This test is ignored, because the current behavior of step over is that it first
* steps into the declaration (name) of the method that is being stepped over and then
* steps back. So there would be weird line numbers from std lib.
*/ */
@Ignore @Ignore
@Test @Test

View File

@ -79,7 +79,11 @@ object GraalVM {
"org.graalvm.tools" % "profiler-tool" % version "org.graalvm.tools" % "profiler-tool" % version
) )
val toolsPkgs = chromeInspectorPkgs val debugAdapterProtocolPkgs = Seq(
"org.graalvm.tools" % "dap-tool" % version
)
val toolsPkgs = chromeInspectorPkgs ++ debugAdapterProtocolPkgs
// TODO: Add graalvmPython // TODO: Add graalvmPython
val langsPkgs = jsPkgs val langsPkgs = jsPkgs

View File

@ -33,7 +33,7 @@ object JPMSUtils {
* When invoking the `java` command, these modules need to be put on the module-path. * When invoking the `java` command, these modules need to be put on the module-path.
*/ */
val componentModules: Seq[ModuleID] = val componentModules: Seq[ModuleID] =
GraalVM.modules ++ GraalVM.langsPkgs ++ Seq( GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.toolsPkgs ++ Seq(
"org.slf4j" % "slf4j-api" % slf4jVersion, "org.slf4j" % "slf4j-api" % slf4jVersion,
"ch.qos.logback" % "logback-classic" % logbackClassicVersion, "ch.qos.logback" % "logback-classic" % logbackClassicVersion,
"ch.qos.logback" % "logback-core" % logbackClassicVersion "ch.qos.logback" % "logback-core" % logbackClassicVersion