From 95d2638dca08ff64dbf50713a557c6264a0cecb0 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 23 May 2024 22:02:41 +0200 Subject: [PATCH] `--attach-debugger` to start LS with debugger This change adds a simple cli parameter to start Language Server with remote debugging capabilities. Whenever I needed to attach the debbugger a) I had to lookup the non-trivial `agentlib` options b) figure out where to add it and build the whole distribution again. --- .../scala/org/enso/launcher/Launcher.scala | 3 ++- .../launcher/components/LauncherRunner.scala | 21 +++++++++++++---- .../components/LauncherRunnerSpec.scala | 9 +++++--- .../org/enso/projectmanager/boot/Cli.scala | 8 +++++++ .../projectmanager/boot/ProjectManager.scala | 13 +++++++---- .../boot/ProjectManagerOptions.scala | 3 ++- .../projectmanager/boot/configuration.scala | 3 ++- .../ExecutorWithUnlimitedPool.scala | 1 + .../LanguageServerController.scala | 1 + .../LanguageServerDescriptor.scala | 2 ++ .../enso/projectmanager/BaseServerSpec.scala | 7 +++--- .../runner/RunSettings.scala | 4 +++- .../runtimeversionmanager/runner/Runner.scala | 23 +++++++++++++++---- 13 files changed, 74 insertions(+), 24 deletions(-) diff --git a/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala b/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala index d791759639..d3213a7576 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala @@ -306,7 +306,8 @@ case class Launcher(cliOptions: GlobalCLIOptions) { contentRoot, versionOverride, logLevel, - logMasking = cliOptions.internalOptions.logMasking, + logMasking = cliOptions.internalOptions.logMasking, + attachDebugger = false, additionalArguments ) .get, diff --git a/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala b/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala index 0ba483e45e..a7d7565b7f 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala @@ -68,7 +68,8 @@ class LauncherRunner( version, arguments ++ setLogLevelArgs(logLevel, logMasking) ++ additionalArguments, - connectLoggerIfAvailable = true + connectLoggerIfAvailable = true, + attachDebugger = false ) } @@ -127,7 +128,8 @@ class LauncherRunner( version, arguments ++ setLogLevelArgs(logLevel, logMasking) ++ additionalArguments, - connectLoggerIfAvailable = true + connectLoggerIfAvailable = true, + attachDebugger = false ) } @@ -148,6 +150,7 @@ class LauncherRunner( versionOverride: Option[SemVer], logLevel: Level, logMasking: Boolean, + attachDebugger: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = for { @@ -158,6 +161,7 @@ class LauncherRunner( versionOverride, logLevel, logMasking, + attachDebugger, additionalArguments ) } yield runSettings @@ -190,7 +194,12 @@ class LauncherRunner( } ( - RunSettings(version, arguments, connectLoggerIfAvailable = false), + RunSettings( + version, + arguments, + connectLoggerIfAvailable = false, + attachDebugger = false + ), whichEngine ) } @@ -239,7 +248,8 @@ class LauncherRunner( version, arguments ++ setLogLevelArgs(logLevel, logMasking) ++ additionalArguments, - connectLoggerIfAvailable = true + connectLoggerIfAvailable = true, + attachDebugger = false ) } @@ -286,7 +296,8 @@ class LauncherRunner( version, arguments ++ setLogLevelArgs(logLevel, logMasking) ++ additionalArguments, - connectLoggerIfAvailable = true + connectLoggerIfAvailable = true, + attachDebugger = false ) } } diff --git a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala index eeeff4b1d2..20d99d0d88 100644 --- a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala +++ b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala @@ -64,7 +64,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { val runSettings = RunSettings( SemVer.of(0, 0, 0), Seq("arg1", "--flag2"), - connectLoggerIfAvailable = true + connectLoggerIfAvailable = true, + attachDebugger = false ) val jvmOptions = Seq(("locally-added-options", "value1")) @@ -299,7 +300,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { versionOverride = None, additionalArguments = Seq("additional"), logLevel = Level.INFO, - logMasking = true + logMasking = true, + attachDebugger = false ) .get @@ -321,7 +323,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { versionOverride = Some(overridden), additionalArguments = Seq(), logLevel = Level.INFO, - logMasking = true + logMasking = true, + attachDebugger = false ) .get .engineVersion shouldEqual overridden diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Cli.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Cli.scala index b354466563..0f1ad5fca9 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Cli.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Cli.scala @@ -14,6 +14,7 @@ object Cli { val VERSION_OPTION = "version" val PROFILING_PATH = "profiling-path" val PROFILING_TIME = "profiling-time" + val ATTACH_DEBUGGER = "attach-debugger" val PROJECTS_DIRECTORY = "projects-directory" val PROJECT_LIST = "project-list" @@ -137,6 +138,12 @@ object Cli { .longOpt(FILESYSTEM_WRITE_PATH) .desc("Write data from stdin to the provided file") .build() + + val attachDebugger: cli.Option = cli.Option.builder + .hasArg(false) + .longOpt(ATTACH_DEBUGGER) + .desc("Enables remote debugging capabilities") + .build() } val options: cli.Options = @@ -148,6 +155,7 @@ object Cli { .addOption(option.noLogMasking) .addOption(option.profilingPath) .addOption(option.profilingTime) + .addOption(option.attachDebugger) .addOption(option.projectsDirectory) .addOption(option.projectList) .addOption(option.filesystemList) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala index 79a1be76ee..ccafa827b0 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala @@ -201,10 +201,14 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { ) } + val parseAttachDebugger = + ZIO.attempt(options.hasOption(Cli.ATTACH_DEBUGGER)) + for { - profilingPath <- parseProfilingPath - profilingTime <- parseProfilingTime - } yield ProjectManagerOptions(profilingPath, profilingTime) + profilingPath <- parseProfilingPath + profilingTime <- parseProfilingTime + attachDebugger <- parseAttachDebugger + } yield ProjectManagerOptions(profilingPath, profilingTime, attachDebugger) } /** The main function of the application, which will be passed the command-line @@ -277,7 +281,8 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { procConf = MainProcessConfig( logLevel, opts.profilingPath, - opts.profilingTime + opts.profilingTime, + opts.attachDebugger ) exitCode <- mainProcess(procConf).fold( th => { diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManagerOptions.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManagerOptions.scala index 02256eb753..987e342bc4 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManagerOptions.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManagerOptions.scala @@ -11,5 +11,6 @@ import scala.concurrent.duration.FiniteDuration */ case class ProjectManagerOptions( profilingPath: Option[Path], - profilingTime: Option[FiniteDuration] + profilingTime: Option[FiniteDuration], + attachDebugger: Boolean ) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala index b60327e28b..46740ec326 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala @@ -18,7 +18,8 @@ object configuration { case class MainProcessConfig( logLevel: Level, profilingPath: Option[Path], - profilingTime: Option[FiniteDuration] + profilingTime: Option[FiniteDuration], + attachDebugger: Boolean ) /** A configuration object for properties of the Project Manager. diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala index 790b3c0215..1d0d37a3cb 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala @@ -122,6 +122,7 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor { version = descriptor.engineVersion, logLevel = inheritedLogLevel, logMasking = Masking.isMaskingEnabled, + attachDebugger = descriptor.attachDebugger, additionalArguments = additionalArguments ) .get diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala index 2e0161ab7a..603ba826a2 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerController.scala @@ -88,6 +88,7 @@ class LanguageServerController( discardOutput = distributionConfiguration.shouldDiscardChildOutput, profilingPath = processConfig.profilingPath, profilingTime = processConfig.profilingTime, + attachDebugger = processConfig.attachDebugger, deferredLoggingServiceEndpoint = loggingServiceDescriptor.getEndpoint, skipGraalVMUpdater = bootloaderConfig.skipGraalVMUpdater ) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala index 53691948b1..5a43ad0fc8 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala @@ -26,6 +26,7 @@ import scala.concurrent.duration.FiniteDuration * printed to parent's streams * @param profilingPath the language server profiling file path * @param profilingTime the time limiting the profiling duration + * @param attachDebugger if true, language server will be started with remote debugging capabilities * @param deferredLoggingServiceEndpoint a future that is completed once the * logging service has been fully set-up; * if the child component should connect @@ -45,6 +46,7 @@ case class LanguageServerDescriptor( discardOutput: Boolean, profilingPath: Option[Path], profilingTime: Option[FiniteDuration], + attachDebugger: Boolean, deferredLoggingServiceEndpoint: Future[Option[URI]], skipGraalVMUpdater: Boolean ) diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala index c161b04e93..97b3b880c0 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala @@ -88,9 +88,10 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll { val processConfig: MainProcessConfig = MainProcessConfig( - logLevel = if (debugLogs) Level.TRACE else Level.ERROR, - profilingPath = profilingPath, - profilingTime = None + logLevel = if (debugLogs) Level.TRACE else Level.ERROR, + profilingPath = profilingPath, + profilingTime = None, + attachDebugger = false ) val testClock = diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/RunSettings.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/RunSettings.scala index 9f9d0ad592..83f1c5314e 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/RunSettings.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/RunSettings.scala @@ -8,9 +8,11 @@ import org.enso.semver.SemVer * @param runnerArguments arguments that should be passed to the runner * @param connectLoggerIfAvailable specifies if the ran component should * connect to launcher's logging service + * @param attachDebugger if true, indicates that the runner should be started with remote debugging capabilities */ case class RunSettings( engineVersion: SemVer, runnerArguments: Seq[String], - connectLoggerIfAvailable: Boolean + connectLoggerIfAvailable: Boolean, + attachDebugger: Boolean ) diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala index 0996182b4b..25c6e8af23 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala @@ -79,7 +79,12 @@ class Runner( engineVersion ) } - RunSettings(engineVersion, arguments, connectLoggerIfAvailable = false) + RunSettings( + engineVersion, + arguments, + connectLoggerIfAvailable = false, + attachDebugger = false + ) } /** Creates [[RunSettings]] for launching the Language Server. */ @@ -89,6 +94,7 @@ class Runner( versionOverride: Option[SemVer], logLevel: Level, logMasking: Boolean, + attachDebugger: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = { val version = resolveVersion(versionOverride, Some(project)) @@ -99,6 +105,7 @@ class Runner( version, logLevel, logMasking, + attachDebugger, additionalArguments ) } @@ -110,6 +117,7 @@ class Runner( version: SemVer, logLevel: Level, logMasking: Boolean, + attachDebugger: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = Try { @@ -137,7 +145,8 @@ class Runner( RunSettings( version, arguments ++ additionalArguments, - connectLoggerIfAvailable = true + connectLoggerIfAvailable = true, + attachDebugger = attachDebugger ) } @@ -221,9 +230,13 @@ class Runner( forceLoggerConnectionArguments() else Seq() - val command = Seq( - javaCommand.executableName - ) ++ jvmArguments ++ loggingConnectionArguments ++ runSettings.runnerArguments + val command = Seq(javaCommand.executableName) ++ + (if (runSettings.attachDebugger) + Seq( + "-agentlib:jdwp=transport=dt_socket,server=y,address=localhost:5005,suspend=y" + ) + else Seq()) ++ + jvmArguments ++ loggingConnectionArguments ++ runSettings.runnerArguments val distributionSettings = distributionManager.getEnvironmentToInheritSettings