Skip to content

Commit afef97c

Browse files
committed
Introduce debugMode in RunnerOrchestration
debugMode allows to connect to runner processes with JDI
1 parent 174a757 commit afef97c

File tree

3 files changed

+29
-10
lines changed

3 files changed

+29
-10
lines changed

compiler/test/dotty/tools/debug/DebugTests.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ object DebugTests extends ParallelTesting:
2424
def testFilter = Properties.testsFilter
2525
def updateCheckFiles: Boolean = Properties.testsUpdateCheckfile
2626
def failedTests = TestReporter.lastRunFailedTests
27+
override def debugMode = true
2728

2829
implicit val summaryReport: SummaryReporting = new SummaryReport
2930

@@ -40,7 +41,7 @@ object DebugTests extends ParallelTesting:
4041

4142
private def verifyDebug(dir: JFile, testSource: TestSource, warnings: Int, reporters: Seq[TestReporter], logger: LoggedRunnable) =
4243
if Properties.testsNoRun then addNoRunWarning()
43-
else runMain(testSource.runClassPath, testSource.allToolArgs) match
44+
else runMain(testSource.runClassPath) match
4445
case Success(output) => ()
4546
case Failure(output) =>
4647
if output == "" then

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
919919

920920
private def verifyOutput(checkFile: Option[JFile], dir: JFile, testSource: TestSource, warnings: Int, reporters: Seq[TestReporter], logger: LoggedRunnable) = {
921921
if Properties.testsNoRun then addNoRunWarning()
922-
else runMain(testSource.runClassPath, testSource.allToolArgs) match {
922+
else runMain(testSource.runClassPath) match {
923923
case Success(output) => checkFile match {
924924
case Some(file) if file.exists => diffTest(testSource, file, output.linesIterator.toList, reporters, logger)
925925
case _ =>

compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package vulpix
44

55
import scala.language.unsafeNulls
66

7-
import java.io.{ File => JFile, InputStreamReader, BufferedReader, PrintStream }
7+
import java.io.{ File => JFile, InputStreamReader, IOException, BufferedReader, PrintStream }
88
import java.nio.file.Paths
99
import java.nio.charset.StandardCharsets
1010
import java.util.concurrent.atomic.AtomicBoolean
@@ -48,8 +48,11 @@ trait RunnerOrchestration {
4848
/** Destroy and respawn process after each test */
4949
def safeMode: Boolean
5050

51+
/** Open JDI connection for testing the debugger */
52+
def debugMode: Boolean = false
53+
5154
/** Running a `Test` class's main method from the specified `dir` */
52-
def runMain(classPath: String, toolArgs: ToolArgs)(implicit summaryReport: SummaryReporting): Status =
55+
def runMain(classPath: String)(implicit summaryReport: SummaryReporting): Status =
5356
monitor.runMain(classPath)
5457

5558
/** Kill all processes */
@@ -70,7 +73,7 @@ trait RunnerOrchestration {
7073
def runMain(classPath: String)(implicit summaryReport: SummaryReporting): Status =
7174
withRunner(_.runMain(classPath))
7275

73-
private class Runner(private var process: Process) {
76+
private class Runner(private var process: RunnerProcess) {
7477
private var childStdout: BufferedReader = uninitialized
7578
private var childStdin: PrintStream = uninitialized
7679

@@ -114,7 +117,7 @@ trait RunnerOrchestration {
114117
}
115118

116119
if (childStdin eq null)
117-
childStdin = new PrintStream(process.getOutputStream, /* autoFlush = */ true)
120+
childStdin = new PrintStream(process.getOutputStream(), /* autoFlush = */ true)
118121

119122
// pass file to running process
120123
childStdin.println(classPath)
@@ -124,7 +127,7 @@ trait RunnerOrchestration {
124127
val sb = new StringBuilder
125128

126129
if (childStdout eq null)
127-
childStdout = new BufferedReader(new InputStreamReader(process.getInputStream, StandardCharsets.UTF_8))
130+
childStdout = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))
128131

129132
var childOutput: String = childStdout.readLine()
130133

@@ -138,7 +141,7 @@ trait RunnerOrchestration {
138141
childOutput = childStdout.readLine()
139142
}
140143

141-
if (process.isAlive && childOutput != null) Success(sb.toString)
144+
if (process.isAlive() && childOutput != null) Success(sb.toString)
142145
else Failure(sb.toString)
143146
}
144147

@@ -159,18 +162,33 @@ trait RunnerOrchestration {
159162
}
160163
}
161164

165+
// A Java process and its JDI port for debugging, if debugMode is enabled.
166+
private class RunnerProcess(p: Process, val port: Option[Int]):
167+
export p.*
168+
162169
/** Create a process which has the classpath of the `ChildJVMMain` and the
163170
* scala library.
164171
*/
165-
private def createProcess: Process = {
172+
private def createProcess: RunnerProcess = {
166173
val url = classOf[ChildJVMMain].getProtectionDomain.getCodeSource.getLocation
167174
val cp = Paths.get(url.toURI).toString + JFile.pathSeparator + Properties.scalaLibrary
168175
val javaBin = Paths.get(sys.props("java.home"), "bin", "java").toString
169-
new ProcessBuilder(javaBin, "-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US", "-Xmx1g", "-cp", cp, "dotty.tools.vulpix.ChildJVMMain")
176+
val args = Seq("-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US", "-Xmx1g", "-cp", cp) ++
177+
(if debugMode then Seq("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,quiet=n") else Seq.empty)
178+
val command = (javaBin +: args) :+ "dotty.tools.vulpix.ChildJVMMain"
179+
val process = new ProcessBuilder(command*)
170180
.redirectErrorStream(true)
171181
.redirectInput(ProcessBuilder.Redirect.PIPE)
172182
.redirectOutput(ProcessBuilder.Redirect.PIPE)
173183
.start()
184+
185+
val jdiPort = Option.when(debugMode):
186+
val reader = new BufferedReader(new InputStreamReader(process.getInputStream, StandardCharsets.UTF_8))
187+
reader.readLine() match
188+
case s"Listening for transport dt_socket at address: $port" => port.toInt
189+
case line => throw new IOException(s"Failed getting JDI port of child JVM: got $line")
190+
191+
RunnerProcess(process, jdiPort)
174192
}
175193

176194
private val freeRunners = mutable.Queue.empty[Runner]

0 commit comments

Comments
 (0)