Ensure the coroutines debug agent attaches even with no stdlib#4588
Merged
dkhalanskyjb merged 2 commits intoMar 5, 2026
Merged
Conversation
mvicsokolova
approved these changes
Jan 14, 2026
Contributor
|
Thank you! I've tested this change on the debugger side |
fzhinkin
requested changes
Feb 17, 2026
fzhinkin
left a comment
Contributor
There was a problem hiding this comment.
Build scripts could be improved to provide more context if the test will fail, but otherwise the change looks good.
Comment on lines
+22
to
+39
| val agentJar = System.getProperty("coroutines.debug.agent.path") | ||
| val errorOutputStream = ByteArrayOutputStream() | ||
| val standardOutputStream = ByteArrayOutputStream() | ||
|
|
||
| project.javaexec { | ||
| mainClass.set("Main") | ||
| classpath = sourceSets.main.get().runtimeClasspath | ||
| jvmArgs = listOf("-javaagent:$agentJar") | ||
| errorOutput = errorOutputStream | ||
| standardOutput = standardOutputStream | ||
| } | ||
|
|
||
| val expectedAgentError = | ||
| "kotlinx.coroutines debug agent failed to load.\n" + | ||
| "Please ensure that the Kotlin standard library is present in the classpath.\n" + | ||
| "Alternatively, you can disable kotlinx.coroutines debug agent by removing `-javaagent=/path/kotlinx-coroutines-core.jar` from your VM arguments.\n" | ||
| val errorOutput = errorOutputStream.toString() | ||
| val standardOutput = standardOutputStream.toString() | ||
| if (!errorOutput.contains(expectedAgentError)) { | ||
| throw GradleException("':safeDebugAgentTest:runWithExpectedFailure' completed with an unexpected output:\n" + standardOutput + "\n" + errorOutput) | ||
| check(errorOutputStream.toString().isEmpty()) { | ||
| "Expected no output in the error stream, but got:\n$errorOutputStream" | ||
| } | ||
| check(standardOutputStream.toString().contains("OK!")) { | ||
| "Expected 'OK!' in the standard output, but got:\n$standardOutputStream" | ||
| } |
Contributor
There was a problem hiding this comment.
Suggested change
| val agentJar = System.getProperty("coroutines.debug.agent.path") | |
| val errorOutputStream = ByteArrayOutputStream() | |
| val standardOutputStream = ByteArrayOutputStream() | |
| project.javaexec { | |
| mainClass.set("Main") | |
| classpath = sourceSets.main.get().runtimeClasspath | |
| jvmArgs = listOf("-javaagent:$agentJar") | |
| errorOutput = errorOutputStream | |
| standardOutput = standardOutputStream | |
| } | |
| val expectedAgentError = | |
| "kotlinx.coroutines debug agent failed to load.\n" + | |
| "Please ensure that the Kotlin standard library is present in the classpath.\n" + | |
| "Alternatively, you can disable kotlinx.coroutines debug agent by removing `-javaagent=/path/kotlinx-coroutines-core.jar` from your VM arguments.\n" | |
| val errorOutput = errorOutputStream.toString() | |
| val standardOutput = standardOutputStream.toString() | |
| if (!errorOutput.contains(expectedAgentError)) { | |
| throw GradleException("':safeDebugAgentTest:runWithExpectedFailure' completed with an unexpected output:\n" + standardOutput + "\n" + errorOutput) | |
| check(errorOutputStream.toString().isEmpty()) { | |
| "Expected no output in the error stream, but got:\n$errorOutputStream" | |
| } | |
| check(standardOutputStream.toString().contains("OK!")) { | |
| "Expected 'OK!' in the standard output, but got:\n$standardOutputStream" | |
| } | |
| val agentJar = System.getProperty("coroutines.debug.agent.path") | |
| val execResult = project.providers.javaexec { | |
| mainClass.set("Main") | |
| classpath = sourceSets.main.get().runtimeClasspath | |
| jvmArgs = listOf("-javaagent:$agentJar") | |
| isIgnoreExitValue = true | |
| } | |
| val exitCode = execResult.result.get().exitValue | |
| val stdout = execResult.standardOutput.asText.getOrElse("<not found>") | |
| val stderr = execResult.standardError.asText.getOrElse("<not found>") | |
| check (exitCode == 0) { | |
| "Process execution ended with non-zero exit code: $exitCode\nstdout:\n$stdout\nstderr:\n$stderr" | |
| } | |
| check(stderr.isEmpty()) { | |
| "Expected no output in the error stream, but got:\n$stderr" | |
| } | |
| check(stdout.contains("OK!")) { | |
| "Expected 'OK!' in the standard output, but got:\n$stdout" | |
| } |
There were a few issues:
Project.javaexecis deprecated and it's better to use a supported alternative- With original javaexec settings, any non-zero exit code would result in an exception with "non-zero exit code" as a result, but without any details and without stdout/err contents.
Collaborator
Author
There was a problem hiding this comment.
Applied the suggestion. Github's "Commit suggestion" refused to do that, saying that it can't apply suggestions to removed lines.
With this change, we ensure that `AgentPremain` does not touch
any of the `kotlin.*` APIs when it's being attached to a process.
This means that even if it's attached by a mistake to a pure Java
process, it won't cause any harm.
Most of the changes are limited to removing non-idiomatic
`runCatching` calls, but one sad consequence is the inability
to use the convenient `thread { }` function.
An integration test was implemented checking that a
pure Java process completes successfully even with the debug agent
attached to it.
The earlier version of the test performed the check even before
the Java class got compiled, which was fine, given that
the execution never even got to the main class and crashed during
the debug agent attach, so it had to be significantly changed.
ef2b842 to
3340c6a
Compare
fzhinkin
approved these changes
Mar 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Alternative for #4565
With this change, we ensure that
AgentPremaindoes not touch any of thekotlin.*APIs when it's being attached to a process. This means that even if it's attached by a mistake to a pure Java process, it won't cause any harm.Most of the changes are limited to removing the
runCatchingantipattern, but one sad consequence is the inability to use the convenientthread { }function.An integration test was implemented, checking that a pure Java process completes successfully even with the debug agent attached to it.
The earlier version of the test performed the check even before the Java class got compiled. It was fine, given that the execution never even got to the main class and crashed during the debug agent attach. With the proposed change, however, it had to be significantly modified.