--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.
This commit is contained in:
Hubert Plociniczak 2024-05-23 22:02:41 +02:00
parent 094076a9e8
commit 95d2638dca
13 changed files with 74 additions and 24 deletions

View File

@ -306,7 +306,8 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
contentRoot, contentRoot,
versionOverride, versionOverride,
logLevel, logLevel,
logMasking = cliOptions.internalOptions.logMasking, logMasking = cliOptions.internalOptions.logMasking,
attachDebugger = false,
additionalArguments additionalArguments
) )
.get, .get,

View File

@ -68,7 +68,8 @@ class LauncherRunner(
version, version,
arguments ++ setLogLevelArgs(logLevel, logMasking) arguments ++ setLogLevelArgs(logLevel, logMasking)
++ additionalArguments, ++ additionalArguments,
connectLoggerIfAvailable = true connectLoggerIfAvailable = true,
attachDebugger = false
) )
} }
@ -127,7 +128,8 @@ class LauncherRunner(
version, version,
arguments ++ setLogLevelArgs(logLevel, logMasking) arguments ++ setLogLevelArgs(logLevel, logMasking)
++ additionalArguments, ++ additionalArguments,
connectLoggerIfAvailable = true connectLoggerIfAvailable = true,
attachDebugger = false
) )
} }
@ -148,6 +150,7 @@ class LauncherRunner(
versionOverride: Option[SemVer], versionOverride: Option[SemVer],
logLevel: Level, logLevel: Level,
logMasking: Boolean, logMasking: Boolean,
attachDebugger: Boolean,
additionalArguments: Seq[String] additionalArguments: Seq[String]
): Try[RunSettings] = ): Try[RunSettings] =
for { for {
@ -158,6 +161,7 @@ class LauncherRunner(
versionOverride, versionOverride,
logLevel, logLevel,
logMasking, logMasking,
attachDebugger,
additionalArguments additionalArguments
) )
} yield runSettings } yield runSettings
@ -190,7 +194,12 @@ class LauncherRunner(
} }
( (
RunSettings(version, arguments, connectLoggerIfAvailable = false), RunSettings(
version,
arguments,
connectLoggerIfAvailable = false,
attachDebugger = false
),
whichEngine whichEngine
) )
} }
@ -239,7 +248,8 @@ class LauncherRunner(
version, version,
arguments ++ setLogLevelArgs(logLevel, logMasking) arguments ++ setLogLevelArgs(logLevel, logMasking)
++ additionalArguments, ++ additionalArguments,
connectLoggerIfAvailable = true connectLoggerIfAvailable = true,
attachDebugger = false
) )
} }
@ -286,7 +296,8 @@ class LauncherRunner(
version, version,
arguments ++ setLogLevelArgs(logLevel, logMasking) arguments ++ setLogLevelArgs(logLevel, logMasking)
++ additionalArguments, ++ additionalArguments,
connectLoggerIfAvailable = true connectLoggerIfAvailable = true,
attachDebugger = false
) )
} }
} }

View File

@ -64,7 +64,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec {
val runSettings = RunSettings( val runSettings = RunSettings(
SemVer.of(0, 0, 0), SemVer.of(0, 0, 0),
Seq("arg1", "--flag2"), Seq("arg1", "--flag2"),
connectLoggerIfAvailable = true connectLoggerIfAvailable = true,
attachDebugger = false
) )
val jvmOptions = Seq(("locally-added-options", "value1")) val jvmOptions = Seq(("locally-added-options", "value1"))
@ -299,7 +300,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec {
versionOverride = None, versionOverride = None,
additionalArguments = Seq("additional"), additionalArguments = Seq("additional"),
logLevel = Level.INFO, logLevel = Level.INFO,
logMasking = true logMasking = true,
attachDebugger = false
) )
.get .get
@ -321,7 +323,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec {
versionOverride = Some(overridden), versionOverride = Some(overridden),
additionalArguments = Seq(), additionalArguments = Seq(),
logLevel = Level.INFO, logLevel = Level.INFO,
logMasking = true logMasking = true,
attachDebugger = false
) )
.get .get
.engineVersion shouldEqual overridden .engineVersion shouldEqual overridden

View File

@ -14,6 +14,7 @@ object Cli {
val VERSION_OPTION = "version" val VERSION_OPTION = "version"
val PROFILING_PATH = "profiling-path" val PROFILING_PATH = "profiling-path"
val PROFILING_TIME = "profiling-time" val PROFILING_TIME = "profiling-time"
val ATTACH_DEBUGGER = "attach-debugger"
val PROJECTS_DIRECTORY = "projects-directory" val PROJECTS_DIRECTORY = "projects-directory"
val PROJECT_LIST = "project-list" val PROJECT_LIST = "project-list"
@ -137,6 +138,12 @@ object Cli {
.longOpt(FILESYSTEM_WRITE_PATH) .longOpt(FILESYSTEM_WRITE_PATH)
.desc("Write data from stdin to the provided file") .desc("Write data from stdin to the provided file")
.build() .build()
val attachDebugger: cli.Option = cli.Option.builder
.hasArg(false)
.longOpt(ATTACH_DEBUGGER)
.desc("Enables remote debugging capabilities")
.build()
} }
val options: cli.Options = val options: cli.Options =
@ -148,6 +155,7 @@ object Cli {
.addOption(option.noLogMasking) .addOption(option.noLogMasking)
.addOption(option.profilingPath) .addOption(option.profilingPath)
.addOption(option.profilingTime) .addOption(option.profilingTime)
.addOption(option.attachDebugger)
.addOption(option.projectsDirectory) .addOption(option.projectsDirectory)
.addOption(option.projectList) .addOption(option.projectList)
.addOption(option.filesystemList) .addOption(option.filesystemList)

View File

@ -201,10 +201,14 @@ object ProjectManager extends ZIOAppDefault with LazyLogging {
) )
} }
val parseAttachDebugger =
ZIO.attempt(options.hasOption(Cli.ATTACH_DEBUGGER))
for { for {
profilingPath <- parseProfilingPath profilingPath <- parseProfilingPath
profilingTime <- parseProfilingTime profilingTime <- parseProfilingTime
} yield ProjectManagerOptions(profilingPath, profilingTime) attachDebugger <- parseAttachDebugger
} yield ProjectManagerOptions(profilingPath, profilingTime, attachDebugger)
} }
/** The main function of the application, which will be passed the command-line /** 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( procConf = MainProcessConfig(
logLevel, logLevel,
opts.profilingPath, opts.profilingPath,
opts.profilingTime opts.profilingTime,
opts.attachDebugger
) )
exitCode <- mainProcess(procConf).fold( exitCode <- mainProcess(procConf).fold(
th => { th => {

View File

@ -11,5 +11,6 @@ import scala.concurrent.duration.FiniteDuration
*/ */
case class ProjectManagerOptions( case class ProjectManagerOptions(
profilingPath: Option[Path], profilingPath: Option[Path],
profilingTime: Option[FiniteDuration] profilingTime: Option[FiniteDuration],
attachDebugger: Boolean
) )

View File

@ -18,7 +18,8 @@ object configuration {
case class MainProcessConfig( case class MainProcessConfig(
logLevel: Level, logLevel: Level,
profilingPath: Option[Path], profilingPath: Option[Path],
profilingTime: Option[FiniteDuration] profilingTime: Option[FiniteDuration],
attachDebugger: Boolean
) )
/** A configuration object for properties of the Project Manager. /** A configuration object for properties of the Project Manager.

View File

@ -122,6 +122,7 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor {
version = descriptor.engineVersion, version = descriptor.engineVersion,
logLevel = inheritedLogLevel, logLevel = inheritedLogLevel,
logMasking = Masking.isMaskingEnabled, logMasking = Masking.isMaskingEnabled,
attachDebugger = descriptor.attachDebugger,
additionalArguments = additionalArguments additionalArguments = additionalArguments
) )
.get .get

View File

@ -88,6 +88,7 @@ class LanguageServerController(
discardOutput = distributionConfiguration.shouldDiscardChildOutput, discardOutput = distributionConfiguration.shouldDiscardChildOutput,
profilingPath = processConfig.profilingPath, profilingPath = processConfig.profilingPath,
profilingTime = processConfig.profilingTime, profilingTime = processConfig.profilingTime,
attachDebugger = processConfig.attachDebugger,
deferredLoggingServiceEndpoint = loggingServiceDescriptor.getEndpoint, deferredLoggingServiceEndpoint = loggingServiceDescriptor.getEndpoint,
skipGraalVMUpdater = bootloaderConfig.skipGraalVMUpdater skipGraalVMUpdater = bootloaderConfig.skipGraalVMUpdater
) )

View File

@ -26,6 +26,7 @@ import scala.concurrent.duration.FiniteDuration
* printed to parent's streams * printed to parent's streams
* @param profilingPath the language server profiling file path * @param profilingPath the language server profiling file path
* @param profilingTime the time limiting the profiling duration * @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 * @param deferredLoggingServiceEndpoint a future that is completed once the
* logging service has been fully set-up; * logging service has been fully set-up;
* if the child component should connect * if the child component should connect
@ -45,6 +46,7 @@ case class LanguageServerDescriptor(
discardOutput: Boolean, discardOutput: Boolean,
profilingPath: Option[Path], profilingPath: Option[Path],
profilingTime: Option[FiniteDuration], profilingTime: Option[FiniteDuration],
attachDebugger: Boolean,
deferredLoggingServiceEndpoint: Future[Option[URI]], deferredLoggingServiceEndpoint: Future[Option[URI]],
skipGraalVMUpdater: Boolean skipGraalVMUpdater: Boolean
) )

View File

@ -88,9 +88,10 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll {
val processConfig: MainProcessConfig = val processConfig: MainProcessConfig =
MainProcessConfig( MainProcessConfig(
logLevel = if (debugLogs) Level.TRACE else Level.ERROR, logLevel = if (debugLogs) Level.TRACE else Level.ERROR,
profilingPath = profilingPath, profilingPath = profilingPath,
profilingTime = None profilingTime = None,
attachDebugger = false
) )
val testClock = val testClock =

View File

@ -8,9 +8,11 @@ import org.enso.semver.SemVer
* @param runnerArguments arguments that should be passed to the runner * @param runnerArguments arguments that should be passed to the runner
* @param connectLoggerIfAvailable specifies if the ran component should * @param connectLoggerIfAvailable specifies if the ran component should
* connect to launcher's logging service * connect to launcher's logging service
* @param attachDebugger if true, indicates that the runner should be started with remote debugging capabilities
*/ */
case class RunSettings( case class RunSettings(
engineVersion: SemVer, engineVersion: SemVer,
runnerArguments: Seq[String], runnerArguments: Seq[String],
connectLoggerIfAvailable: Boolean connectLoggerIfAvailable: Boolean,
attachDebugger: Boolean
) )

View File

@ -79,7 +79,12 @@ class Runner(
engineVersion engineVersion
) )
} }
RunSettings(engineVersion, arguments, connectLoggerIfAvailable = false) RunSettings(
engineVersion,
arguments,
connectLoggerIfAvailable = false,
attachDebugger = false
)
} }
/** Creates [[RunSettings]] for launching the Language Server. */ /** Creates [[RunSettings]] for launching the Language Server. */
@ -89,6 +94,7 @@ class Runner(
versionOverride: Option[SemVer], versionOverride: Option[SemVer],
logLevel: Level, logLevel: Level,
logMasking: Boolean, logMasking: Boolean,
attachDebugger: Boolean,
additionalArguments: Seq[String] additionalArguments: Seq[String]
): Try[RunSettings] = { ): Try[RunSettings] = {
val version = resolveVersion(versionOverride, Some(project)) val version = resolveVersion(versionOverride, Some(project))
@ -99,6 +105,7 @@ class Runner(
version, version,
logLevel, logLevel,
logMasking, logMasking,
attachDebugger,
additionalArguments additionalArguments
) )
} }
@ -110,6 +117,7 @@ class Runner(
version: SemVer, version: SemVer,
logLevel: Level, logLevel: Level,
logMasking: Boolean, logMasking: Boolean,
attachDebugger: Boolean,
additionalArguments: Seq[String] additionalArguments: Seq[String]
): Try[RunSettings] = ): Try[RunSettings] =
Try { Try {
@ -137,7 +145,8 @@ class Runner(
RunSettings( RunSettings(
version, version,
arguments ++ additionalArguments, arguments ++ additionalArguments,
connectLoggerIfAvailable = true connectLoggerIfAvailable = true,
attachDebugger = attachDebugger
) )
} }
@ -221,9 +230,13 @@ class Runner(
forceLoggerConnectionArguments() forceLoggerConnectionArguments()
else Seq() else Seq()
val command = Seq( val command = Seq(javaCommand.executableName) ++
javaCommand.executableName (if (runSettings.attachDebugger)
) ++ jvmArguments ++ loggingConnectionArguments ++ runSettings.runnerArguments Seq(
"-agentlib:jdwp=transport=dt_socket,server=y,address=localhost:5005,suspend=y"
)
else Seq()) ++
jvmArguments ++ loggingConnectionArguments ++ runSettings.runnerArguments
val distributionSettings = val distributionSettings =
distributionManager.getEnvironmentToInheritSettings distributionManager.getEnvironmentToInheritSettings