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)))
val langs =
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(
"-Dpolyglotimpl.DisableClassPathIsolation=true"

View File

@ -15,6 +15,8 @@ used by Enso, broken up as follows:
Debugger.
- [**Chrome devtools debugger:**](./chrome-devtools.md) A guide how to debug
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
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

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,
RuntimeOptions
}
import org.graalvm.polyglot.Engine
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.{Context, Engine}
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.
*/
@ -131,14 +131,7 @@ class ContextFactory {
if (graalpy.exists()) {
builder.option("python.Executable", graalpy.getAbsolutePath());
}
if (
Engine
.newBuilder()
.allowExperimentalOptions(true)
.build()
.getLanguages()
.containsKey("java")
) {
if (engineHasJava()) {
builder
.option("java.ExposeNativeJavaVM", "true")
.option("java.Polyglot", "true")
@ -148,4 +141,24 @@ class ContextFactory {
}
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());
}
@Ignore
@Test
public void testSteppingOver() {
Source src = createEnsoSource("""
baz x = x # 1
bar x = baz x # 2
foo x = # 3
bar 42 # 4
end = 0 # 5
baz x = x # 1
bar x = # 2
ret = baz x # 3
ret # 4
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());
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
* 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
@Test

View File

@ -79,7 +79,11 @@ object GraalVM {
"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
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.
*/
val componentModules: Seq[ModuleID] =
GraalVM.modules ++ GraalVM.langsPkgs ++ Seq(
GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.toolsPkgs ++ Seq(
"org.slf4j" % "slf4j-api" % slf4jVersion,
"ch.qos.logback" % "logback-classic" % logbackClassicVersion,
"ch.qos.logback" % "logback-core" % logbackClassicVersion