scalacenter / scala-debug-adapter   4.0.4

Apache License 2.0 GitHub

Implementation of the Debug Adapter Protocol for Scala

Scala versions: 3.x 2.13 2.12
sbt plugins: 1.0

scala-debug-adapter

build-badge Maven Central

The scala-debug-adapter is a server-side implementation of the Debug Adapter Protocol for the Scala language running on the JVM platform. It is based on and extends the microsoft/java-debug implementation.

The project originated in the Bloop repository, it is now released independently so that it can be used in other build tools of the Scala ecosystem. For instance, the sbt-debug-adapter is an sbt plugin that provides sbt with the debug adapter capability.

Usage

You can add the scala-debug-adapter as a dependency in your build.sbt:

// build.sbt
scalaVersion := "2.12.16",
libraryDependencies += "ch.epfl.scala" %% "scala-debug-adapter" % "3.0.5"

The scala-debug-adapter expects the Java Debug Interface to be class loaded. While it is always the case with Java 9, you probably want to use the sbt-jdi-tools to be able to run on Java 8 as well:

// project/plugins.sbt
addSbtPlugin("org.scala-debugger" % "sbt-jdi-tools" % "1.1.1")

You can start a debug server by providing your own intance of Debuggee, DebugToolsResolver and Logger.

The Debuggee

The Debuggee is a trait that describes the program that we want to debug.

It contains the list of modules of the current project, the list of libraries, the list of unmanaged entries and the java runtime.

There are a few difference between a module, a library and an unmanaged entry:

  • Module: We know its exact scala version, and its compiler options. They will be used by the debugger to evaluate expressions in the module.
  • Library: We only know its binary version. We can try to evaluate expression but it can fail if the compilation of the library involved some compiler options or plugins.
  • UnmanagedEntry: We have the class files, or class jar, but not the source files, or source jar. We won't be able to debug inside those classpath entries. We still need them to build the full classpath.

The Debuggee trait also contains a run method that is called by the debugger to start the program.

The sbt version of Debuggee can be found here. There also is a Bloop version of Debuggee in scalacenter/bloop.

The DebugToolsResolver

Depending on the Scala versions specified in the Debuggee, the debugger will need to resolve some additionnal tools:

  • The expression compiler: to compile the Scala expression that the user wants to evaluate
  • The step filter: to filter the intermediate steps of execution that are generated by the compiler, among which the mixin-forwarders, the bridges, the getters and setters, the synthetized methods.

The role of the DebugToolsResolver is to resovle the debug tools and their dependencies, and to load them in a class-loader. The debugger takes care of reusing those class-loaders as often as possible: it should call the DebugToolsResolver only once by Scala version.

To implement the DebugToolsResolver you can use the Maven coordinates of the required tools in ch.epfl.scala.debugadapter.BuildInfo.

The sbt version of DebugToolsResolver can be found here. There also is a Bloop version of DebugToolsResolver that uses Coursier in scalacenter/bloop.

Starting the DebugServer

Given that you have an instance debuggee of Debuggee, an instance resolver of DebugToolsResolver and an instance logger of Logger, this is how you can wire things and start the debug server:

// find an available IP socket address
val address = new DebugServer.Address()

// resolve the debug tools needed by the debuggee
val tools = DebugTools(debuggee, resolver, logger)

// create and start the debug server
val server = DebugServer(debugge, tools, logger, address)
server.start()

// return address.uri for the DAP client to connect
address.uri

sbt-debug-adapter

The sbt-debug-adapter is an sbt plugin compatible with sbt 1.4.0 or greater. It provides the sbt server with the BSP debugSession/start endpoint to start a Scala DAP server.

The specification of the debugSession/start endpoint can be found in the Bloop documentation.

Usage

As a global plugin use ~/.sbt/1.0/plugins/plugins.sbt, otherwise add to your local project (e.g. project/plugins.sbt):

// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-debug-adapter" % "1.0.0")

Again the JDI tools must be class loaded, this time by sbt itself. To do so you can use the sbt-jdi-tools plugin in the meta project (it goes to project/project/plugins.sbt).

// project/project/plugins.sbt
addSbtPlugin("org.scala-debugger" % "sbt-jdi-tools" % "1.1.1")

Development

tests

The tests module contains the tests of the Scala Debug Server. It uses the TestingDebugClient, a minimal debug client that is used to communicate with the debug server via a socket.

References

History