Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Enso language in ChromeDev tools with --inspect option #3432

Merged
merged 13 commits into from
May 10, 2022
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
- [Provide `tagValues` for function arguments in the language server][3422]
- [Delay construction of Truffle nodes to speed initialization][3429]
- [Frgaal compiler integration to allow for latest Java constructs][3421]
- [Support for Chrome developer tools --inspect option][3432]
- [Move Builtin Types and Methods definitions to stdlib][3363]

[3227]: https://github.com/enso-org/enso/pull/3227
Expand All @@ -209,6 +210,7 @@
[3422]: https://github.com/enso-org/enso/pull/3422
[3429]: https://github.com/enso-org/enso/pull/3429
[3421]: https://github.com/enso-org/enso/pull/3421
[3432]: https://github.com/enso-org/enso/pull/3432
[3363]: https://github.com/enso-org/enso/pull/3363

# Enso 2.0.0-alpha.18 (2021-10-12)
Expand Down
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ Global / onChangedBuildSource := ReloadOnSourceChanges
ThisBuild / javacOptions ++= Seq(
"-encoding", // Provide explicit encoding (the next line)
"UTF-8", // Specify character encoding used by Java source files.
"-deprecation" // Shows a description of each use or override of a deprecated member or class.
"-deprecation",// Shows a description of each use or override of a deprecated member or class.
"-g" // Include debugging information
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
)

ThisBuild / scalacOptions ++= Seq(
Expand Down Expand Up @@ -1152,6 +1153,8 @@ lazy val runtime = (project in file("engine/runtime"))
"org.graalvm.truffle" % "truffle-api" % graalVersion % Benchmark,
"org.typelevel" %% "cats-core" % catsVersion,
"eu.timepit" %% "refined" % refinedVersion,
"junit" % "junit" % "4.12" % Test,
"com.novocode" % "junit-interface" % "0.11" % Test exclude("junit", "junit-dep"),
// This dependency is needed only so that developers don't download Frgaal manually.
// Sadly it cannot be placed under plugins either because meta dependencies are not easily
// accessible from the non-meta build definition.
Expand Down
18 changes: 14 additions & 4 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,14 @@ withDebug run --dumpGraphs --printAssembly -- --run MyFile.enso
withDebug benchOnly --showCompilations -- RecursionBenchmark
```

Step by step debugging can be triggered as

```
withDebug testOnly --debugger -- *FavoriteTest*
```

read more about [debugging Java & Enso code](debugger/README.md).

#### Working with Assembly

In order to examine the assembly generated by GraalVM and HotSpot you need to
Expand Down Expand Up @@ -480,9 +488,8 @@ filing an issue with us.
`sbt` invocation.
- **Debugging Not Working:** The sbt tasks run the invoked programs in a forked
JVM. This means that to attach a debugger to it you need to use the JVM remote
debugging support. We cannot support all possible configurations for this, but
if you use IntelliJ please see the [Using IntelliJ](#using-intellj) section
above for instructions.
debugging support. Follow [Enso debugging instructions](debugger/README.md) or
see the [Using IntelliJ](#using-intellj) section for instructions.

If your problem was not listed above, please
[file a bug report](https://github.com/enso-org/enso/issues/new?assignees=&labels=Type%3A+Bug&template=bug-report.md&title=)
Expand Down Expand Up @@ -659,8 +666,11 @@ The project manager will look for the appropriate subdirectory in the _engines_
directory of the distribution folder. Distribution paths are printed when you
run project manager with `-v` verbose logging.

Btw. you can specify `ENSO_JVM_OPTS` to turn
[debugging of the Engine runtime](debugger/README.md) on:

```bash
$ export ENSO_JVM_OPTS=-agentlib:jdwp=transport=dt_socket,address=5005 # to turn debugging of Engine runtime on
$ export ENSO_JVM_OPTS=-agentlib:jdwp=transport=dt_socket,address=5005
$ ./built-distribution/enso-project-manager-0.0.0-dev-linux-amd64/enso/bin/project-manager --no-log-masking -v
[info] [2021-06-16T11:49:33.639Z] [org.enso.projectmanager.boot.ProjectManager$] Starting Project Manager...
[debug] [2021-06-16T11:49:33.639Z] [org.enso.runtimeversionmanager.distribution.DistributionManager] Detected paths: DistributionPaths(
Expand Down
54 changes: 53 additions & 1 deletion docs/debugger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ tags: [debugger, repl, readme]
order: 0
---

# Debugger
# Enso Debugger

The Enso Debugger allows amongst other things, to execute arbitrary expressions
in a given execution context - this is used to implement a debugging REPL. The
Expand All @@ -16,3 +16,55 @@ This folder contains all documentation pertaining to the REPL and the debugger,
which is broken up as follows:

- [**The Enso Debugger Protocol:**](./protocol.md) The protocol for the Debugger

# Chrome Developer Tools Debugger

As a well written citizen of the [GraalVM](http://graalvm.org) project the Enso
language can be used with existing tools available for the overall platform. One
of them is
[Chrome Debugger](https://www.graalvm.org/22.1/tools/chrome-debugger/) and Enso
language is fully integrated with it. Launch the `bin/enso` executable with
additional `--inspect` option and debug your Enso programs in _Chrome Developer
Tools_.

```bash
enso$ ./built-distribution/enso-engine-*/enso-*/bin/enso --inspect --run ./test/Tests/src/Data/Numbers_Spec.enso
Debugger listening on ws://127.0.0.1:9229/Wugyrg9
For help, see: https://www.graalvm.org/tools/chrome-debugger
E.g. in Chrome open: devtools://devtools/bundled/js_app.html?ws=127.0.0.1:9229/Wugyrg9
```

copy the printed URL into chrome browser and you should see:

![Chrome Debugger](chrome-debugger.png)

Step in, step over, set breakpoints, watch values of your variables as execution
of your Enso program progresses.

# Debugging Enso and Java Code at Once

Enso libraries are written in a mixture of Enso code and Java libraries.
Debugging both sides (the Java as well as Enso code) is possible with a decent
IDE.

Get [NetBeans](http://netbeans.apache.org) version 13 or newer or
[VS Code with Apache Language Server extension](https://cwiki.apache.org/confluence/display/NETBEANS/Apache+NetBeans+Extension+for+Visual+Studio+Code)
and just pass in special JVM arguments when launching the `bin/enso` launcher:

```bash
enso$ JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,address=8000 ./built-distribution/enso-engine-*/enso-*/bin/enso --run ./test/Tests/src/Data/Numbers_Spec.enso
Listening for transport dt_socket at address: 8000
```

and then _Debug/Attach Debugger_. Once connected suspend the execution and (if
the Enso language has already been started) choose the _Toggle Pause in GraalVM
Script_ button in the toolbar:

![NetBeans Debugger](java-debugger.png)

and your execution shall stop on the next `.enso` line of code. This mode allows
to debug both - the Enso code as well as Java code. The stack traces shows a
mixture of Java and Enso stack frames by default. Right-clicking on the thread
allows one to switch to plain Java view (with a way more stack frames) and back.
Analyzing low level details as well as Enso developer point of view shall be
simple with such tool.
Binary file added docs/debugger/chrome-debugger.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/debugger/java-debugger.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ContextFactory {
* @param strictErrors whether or not to use strict errors
* @param useGlobalIrCacheLocation whether or not to use the global IR cache
* location
* @param options additional options for the Context
* @return configured Context instance
*/
def create(
Expand All @@ -36,9 +37,10 @@ class ContextFactory {
logLevel: LogLevel,
logMasking: Boolean,
enableIrCaches: Boolean,
strictErrors: Boolean = false,
useGlobalIrCacheLocation: Boolean = true,
enableAutoParallelism: Boolean = false
strictErrors: Boolean = false,
useGlobalIrCacheLocation: Boolean = true,
enableAutoParallelism: Boolean = false,
options: java.util.Map[String, String] = java.util.Collections.emptyMap
): PolyglotContext = {
val context = Context
.newBuilder()
Expand All @@ -54,6 +56,7 @@ class ContextFactory {
.option(RuntimeOptions.DISABLE_IR_CACHES, (!enableIrCaches).toString)
.option(DebugServerInfo.ENABLE_OPTION, "true")
.option(RuntimeOptions.LOG_MASKING, logMasking.toString)
.options(options)
.option(
RuntimeOptions.ENABLE_AUTO_PARALLELISM,
enableAutoParallelism.toString
Expand Down
19 changes: 16 additions & 3 deletions engine/runner/src/main/scala/org/enso/runner/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import scala.Console.err
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Success, Try}
import scala.util.control.NonFatal
import java.util.Collections

/** The main CLI entry point class. */
object Main {

private val RUN_OPTION = "run"
private val INSPECT_OPTION = "inspect"
private val HELP_OPTION = "help"
private val NEW_OPTION = "new"
private val PROJECT_NAME_OPTION = "new-project-name"
Expand Down Expand Up @@ -88,6 +90,10 @@ object Main {
.longOpt(RUN_OPTION)
.desc("Runs a specified Enso file.")
.build
val inspect = CliOption.builder
.longOpt(INSPECT_OPTION)
.desc("Start the Chrome inspector when --run is used.")
.build
val docs = CliOption.builder
.longOpt(DOCS_OPTION)
.desc("Runs the Enso documentation generator.")
Expand Down Expand Up @@ -312,6 +318,7 @@ object Main {
.addOption(help)
.addOption(repl)
.addOption(run)
.addOption(inspect)
.addOption(docs)
.addOption(preinstall)
.addOption(newOpt)
Expand Down Expand Up @@ -468,14 +475,16 @@ object Main {
* @param logLevel log level to set for the engine runtime
* @param logMasking is the log masking enabled
* @param enableIrCaches are IR caches enabled
* @param inspect shall inspect option be enabled
*/
private def run(
path: String,
projectPath: Option[String],
logLevel: LogLevel,
logMasking: Boolean,
enableIrCaches: Boolean,
enableAutoParallelism: Boolean
enableAutoParallelism: Boolean,
inspect: Boolean
): Unit = {
val file = new File(path)
if (!file.exists) {
Expand Down Expand Up @@ -506,7 +515,10 @@ object Main {
logMasking,
enableIrCaches,
strictErrors = true,
enableAutoParallelism = enableAutoParallelism
enableAutoParallelism = enableAutoParallelism,
options =
if (inspect) Collections.singletonMap("inspect", "")
else Collections.emptyMap
)
if (projectMode) {
PackageManager.Default.loadPackage(file) match {
Expand Down Expand Up @@ -948,7 +960,8 @@ object Main {
logLevel,
logMasking,
shouldEnableIrCaches(line),
line.hasOption(AUTO_PARALLELISM_OPTION)
line.hasOption(AUTO_PARALLELISM_OPTION),
line.hasOption(INSPECT_OPTION)
)
}
if (line.hasOption(REPL_OPTION)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
DebuggerTags.AlwaysHalt.class,
StandardTags.CallTag.class,
StandardTags.ExpressionTag.class,
StandardTags.StatementTag.class,
StandardTags.RootTag.class,
StandardTags.TryBlockTag.class,
IdentifiedTag.class
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.enso.interpreter.node.callable.function;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo;
import java.util.Set;
import org.enso.interpreter.node.ExpressionNode;

/**
Expand Down Expand Up @@ -48,4 +52,16 @@ public Object executeGeneric(VirtualFrame frame) {
}
return returnExpr.executeGeneric(frame);
}

@Override
public InstrumentableNode materializeInstrumentableNodes(
Set<Class<? extends Tag>> materializedTags) {
if (materializedTags.contains(StandardTags.StatementTag.class)) {
for (int i = 0; i < statements.length; i++) {
statements[i] = insert(StatementNode.wrap(statements[i]));
}
this.returnExpr = insert(StatementNode.wrap(returnExpr));
}
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.enso.interpreter.node.callable.function;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.node.ExpressionNode;

/**
* Node tagged with {@link StandardTags.StatementTag}. Inserted by {@link BlockNode} into the AST
* when debugger is connected.
*/
final class StatementNode extends ExpressionNode {
@Child ExpressionNode node;

private StatementNode(ExpressionNode node) {
this.node = node;
}

static StatementNode wrap(ExpressionNode node) {
if (node instanceof StatementNode statement) {
return statement;
} else {
return new StatementNode(node);
}
}

@Override
public SourceSection getSourceSection() {
return node.getSourceSection();
}

@Override
public boolean isInstrumentable() {
return getSourceSection() != null && node.isInstrumentable();
}

@Override
public Object executeGeneric(VirtualFrame frame) {
return node.executeGeneric(frame);
}

@Override
public boolean hasTag(Class<? extends Tag> tag) {
return StandardTags.StatementTag.class == tag;
}
}
Loading