Skip to content

Not possible to run as agent #4404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pinguinjkeke opened this issue Apr 2, 2025 · 8 comments
Closed

Not possible to run as agent #4404

pinguinjkeke opened this issue Apr 2, 2025 · 8 comments
Labels
docs KDoc and API reference

Comments

@pinguinjkeke
Copy link

Describe the bug
The documentation says:

Debug module can also be used as a standalone JVM agent to enable debug probes on the application startup. You can run your application with an additional argument: -javaagent:kotlinx-coroutines-debug-1.10.1.jar.

When trying to run my spring-boot app with the agent I get the following error:

Exception in thread "main" java.lang.ClassNotFoundException: kotlinx.coroutines.debug.internal.AgentPremain
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
        at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:504)
        at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:572)

The AgentPremain class itself declares that:

/*
 * This class is loaded if and only if kotlinx-coroutines-core was used as -javaagent argument,
 * but Android complains anyway (java.lang.instrument.*), so we suppress all lint checks here
 */

So I assumed that I need to load kotlin-coroutines-core as agent, but I get the following error:

Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/Result
        at kotlinx.coroutines.debug.internal.AgentPremain.<clinit>(AgentPremain.kt:19)
        at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
        at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160)
        at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300)
        at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newMethodAccessor(MethodHandleAccessorFactory.java:71)
        at java.base/jdk.internal.reflect.ReflectionFactory.newMethodAccessor(ReflectionFactory.java:159)
        at java.base/java.lang.reflect.Method.acquireMethodAccessor(Method.java:726)
        at java.base/java.lang.reflect.Method.invoke(Method.java:577)
        at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:560)
        at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:572)
@dkhalanskyjb dkhalanskyjb added question docs KDoc and API reference and removed bug question labels Apr 3, 2025
@dkhalanskyjb
Copy link
Collaborator

These jar files do not contain every class they need. Therefore, the runtime classpath for kotlinx-coroutines-debug-1.10.1.jar has to contain both kotlinx-coroutines-coroutines-1.10.1.jar and the Kotlin standard library jar. The errors you list here show that this isn't the case.

@pinguinjkeke
Copy link
Author

When javaagent attaches, these classes are still not loaded.

As I understand, these classes must be declared in Boot-Class-Path of the manifest of kotlin-coroutines-debug but they are not there.

Anyway, what's the working configuration for javaagent? Can any example be attached to this repository?

@dkhalanskyjb
Copy link
Collaborator

I'm not sure which example you want attached. -javaagent, with the documentation already mentions, is supposed to be enough.

Here's a full step-by-step example that's as simple as possible, without relying on Gradle or anything else except the bare necessities:

  1. Download https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.10.1/kotlinx-coroutines-core-jvm-1.10.1.jar, https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.10.1/kotlinx-coroutines-debug-1.10.1.jar, https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar
  2. Enter the directory where you've downloaded these files. There, create a file called MyFile.kt with the following contents:
import kotlinx.coroutines.*
import kotlin.time.*

fun main() {
    runBlocking {
        delay(Duration.INFINITE)
    }
}
  1. Compile the file using kotlinc -cp kotlinx-coroutines-debug-1.10.1.jar:kotlinx-coroutines-core-jvm-1.10.1.jar MyFile.kt
  2. Run the program using java -cp kotlin-stdlib-2.1.20.jar:kotlinx-coroutines-core-jvm-1.10.1.jar:kotlinx-coroutines-debug-1.10.1.jar:. -javaagent:kotlinx-coroutines-debug-1.10.1.jar MyFileKt. It should hang.
  3. Next, look up the process ID of the hanging java process using pgrep -a java.
  4. Send the signal to dump all coroutines using kill -5 $your_process_id. Expected output is something like this:
Coroutines dump 2025/04/03 13:30:44

Coroutine BlockingCoroutine{Active}@59b2ae17, state: SUSPENDED
	at MyFileKt$main$1.invokeSuspend(MyFile.kt:6)

I've just verified that it works.

@pinguinjkeke
Copy link
Author

pinguinjkeke commented Apr 3, 2025

But what to do in case of nested spring-boot jar when another copy of kotlix-coroutines-core-jvm and kotlin-stdlib are prepacked?

@dkhalanskyjb
Copy link
Collaborator

I still don't see why something should be different in that scenario. If you can show us an example of a project for which the debug agent doesn't work, we will look into the problem and help you.

@pinguinjkeke
Copy link
Author

spring.zip
Here it is. Compile it with ./gradlew :assemble and run using start.sh

@qwwdfsad
Copy link
Member

Thanks for the repro!
This is a known issue for Spring Boot jars: https://stackoverflow.com/questions/60237664/classpath-problems-while-instrumenting-springboot-application

TL;DR: when building a fat-jar, Spring Boot uses its own executable JAR format and the application classes and dependencies are loaded by a special Spring classloader (org.springframework.boot.loader.launch.LaunchedClassLoader).
-javaagent premain is loaded by a bootstrap one and thus has no access to the application classes, leading to obscure errors.

I am not aware of straightforward solutions here (probably worth raising it in Spring Boot repo), only of palliatives:

  • You can use dynamic attachment (DebugProbes.install from the main)
  • Alternatively, it is possible to use your own fat-jar instead of Spring Boot bootJar -- then it is going to be a regular JAR and -javaagent will work as expected
  • The most tricky one -- start up the app with Spring's JarLauncher, but it requires further investigation

@pinguinjkeke
Copy link
Author

Thanks for the response.

Closing it for now as it's more spring-related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs KDoc and API reference
Projects
None yet
Development

No branches or pull requests

3 participants