Skip to content

Please provide artifact with DebugProbes for classpath patching instead of instrumentations. #3356

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
Fuud opened this issue Jul 5, 2022 · 1 comment

Comments

@Fuud
Copy link

Fuud commented Jul 5, 2022

Hi,

I want enable coroutine dump for Production.
I tried add DebugProbes.install() at main class but when I run mvn test, execution was blocked [2]
I cannot use javaagent due to limitation of our deploy system.
Also I am not sure if it will work with well with multiple classloaders (we have some apps deployed as webapps into stand-alone Tomcat server)

As all what DebugProbes.install() instrumentation do is replacing one class with another implementation, I created following class to enable functionality without instrumentation [1]. It is written on Java because Kotlin forbids compile kotlin* packages.

Could we create maven artifact in kotlinx.coroutines repo that can be placed at the beginning of the classpath to enable DebugProbes?

[1] Patch class:

package kotlin.coroutines.jvm.internal;

import kotlin.coroutines.Continuation;
import kotlinx.coroutines.debug.DebugProbes;
import kotlinx.coroutines.debug.internal.AgentInstallationType;

import java.lang.reflect.Method;

public class DebugProbesKt {
    /**
     * This class is defined at https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/jvm/src/kotlin/coroutines/jvm/internal/DebugProbes.kt
     * DebugProbes.install will replace it  with kotlin.coroutines.jvm.internal.DebugProbesKt (via instrumentation and attach api)
     * <p>
     * I found that instrumentation can (dead?)lock the tests.
     * And I decide to change instrumentation to class path patch.
     */

    private static final Method probeCoroutineCreatedMethod;
    private static final Method probeCoroutineResumedMethod;
    private static final Method probeCoroutineSuspended;

    static {
        try {
            // mark that DebugProbes are installed and no instrumentation is needed
            AgentInstallationType.INSTANCE.setInstalledStatically$kotlinx_coroutines_core(true);
            DebugProbes.INSTANCE.install(); // start cleaner thread

            Class<?> delegate = Class.forName("kotlinx.coroutines.debug.internal.DebugProbesKt");
            probeCoroutineCreatedMethod = delegate.getDeclaredMethod("probeCoroutineCreated", Continuation.class);
            probeCoroutineCreatedMethod.setAccessible(true);

            probeCoroutineResumedMethod = delegate.getDeclaredMethod("probeCoroutineResumed", Continuation.class);
            probeCoroutineResumedMethod.setAccessible(true);

            probeCoroutineSuspended = delegate.getDeclaredMethod("probeCoroutineSuspended", Continuation.class);
            probeCoroutineSuspended.setAccessible(true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> Continuation<T> probeCoroutineCreated(Continuation<T> completion) throws Exception {
        return (Continuation<T>) probeCoroutineCreatedMethod.invoke(null, completion);
    }

    public static void probeCoroutineResumed(Continuation<?> frame) throws Exception {
        probeCoroutineResumedMethod.invoke(null, frame);
    }

    public static void probeCoroutineSuspended(Continuation<?> frame) throws Exception {
        probeCoroutineSuspended.invoke(null, frame);
    }
}

[2] Locked tests:

H:\>jstack -F 40748
Attaching to process ID 40748, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.321-b01
Deadlock Detection:

No deadlocks found.

Thread 24: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=144 (Compiled frame)
 - java.lang.ref.ReferenceQueue.remove() @bci=2, line=165 (Compiled frame)
 - kotlinx.coroutines.debug.internal.ConcurrentWeakMap.runWeakRefQueueCleaningLoopUntilInterrupted() @bci=39, line=74 (Interpreted frame)
 - kotlinx.coroutines.debug.internal.DebugProbesImpl$startWeakRefCleanerThread$1.invoke() @bci=3, line=100 (Interpreted frame)
 - kotlinx.coroutines.debug.internal.DebugProbesImpl$startWeakRefCleanerThread$1.invoke() @bci=1, line=99 (Interpreted frame)
 - kotlin.concurrent.ThreadsKt$thread$thread$1.run() @bci=4, line=30 (Interpreted frame)


Thread 22: (state = BLOCKED)
 - sun.misc.Unsafe.park(boolean, long) @bci=0 (Interpreted frame)
 - java.util.concurrent.locks.LockSupport.parkNanos(java.lang.Object, long) @bci=20, line=215 (Interpreted frame)
 - java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(long) @bci=78, line=2083 (Interpreted frame)
 - java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take() @bci=124, line=1093 (Interpreted frame)
 - java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take() @bci=1, line=809 (Interpreted frame)
 - java.util.concurrent.ThreadPoolExecutor.getTask() @bci=149, line=1074 (Interpreted frame)
 - java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) @bci=26, line=1134 (Interpreted frame)
 - java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=5, line=624 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=748 (Interpreted frame)


Thread 21: (state = IN_NATIVE)
 - java.io.FileInputStream.readBytes(byte[], int, int) @bci=0 (Compiled frame; information may be imprecise)
 - java.io.FileInputStream.read(byte[], int, int) @bci=4, line=255 (Compiled frame)
 - java.io.BufferedInputStream.fill() @bci=214, line=246 (Compiled frame)
 - java.io.BufferedInputStream.read() @bci=12, line=265 (Compiled frame)
 - java.io.DataInputStream.readInt() @bci=4, line=387 (Compiled frame)
 - org.apache.maven.surefire.booter.MasterProcessCommand.decode(java.io.DataInputStream) @bci=1, line=115 (Interpreted frame)
 - org.apache.maven.surefire.booter.CommandReader$CommandRunnable.run() @bci=40, line=391 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=748 (Interpreted frame)


Thread 14: (state = BLOCKED)


Thread 13: (state = BLOCKED)


Thread 12: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=144 (Compiled frame)
 - java.lang.ref.ReferenceQueue.remove() @bci=2, line=165 (Compiled frame)
 - java.lang.ref.Finalizer$FinalizerThread.run() @bci=36, line=216 (Interpreted frame)


Thread 11: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.Object.wait() @bci=2, line=502 (Compiled frame)
 - java.lang.ref.Reference.tryHandlePending(boolean) @bci=54, line=191 (Compiled frame)
 - java.lang.ref.Reference$ReferenceHandler.run() @bci=1, line=153 (Interpreted frame)


Thread 1: (state = IN_NATIVE)
 - com.sun.jna.Native.invokeInt(com.sun.jna.Function, long, int, java.lang.Object[]) @bci=0 (Interpreted frame)
 - com.sun.jna.Function.invoke(java.lang.Object[], java.lang.Class, boolean, int) @bci=211, line=426 (Interpreted frame)
 - com.sun.jna.Function.invoke(java.lang.reflect.Method, java.lang.Class[], java.lang.Class, java.lang.Object[], java.util.Map) @bci=271, line=361 (Interpreted frame)
 - com.sun.jna.Library$Handler.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) @bci=390, line=265 (Interpreted frame)
 - com.sun.proxy.$Proxy96.ConnectNamedPipe(com.sun.jna.platform.win32.WinNT$HANDLE, com.sun.jna.platform.win32.WinBase$OVERLAPPED) @bci=20 (Interpreted frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.VirtualMachine$ForHotSpot$Connection$ForJnaWindowsNamedPipe.execute(java.lang.String, java.lang.String[]) @bci=360, line=1045 (Interpreted frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.VirtualMachine$ForHotSpot.load(java.lang.String, boolean, java.lang.String) @bci=61, line=361 (Interpreted frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.VirtualMachine$ForHotSpot.loadAgent(java.lang.String, java.lang.String) @bci=4, line=335 (Interpreted frame)
 - sun.reflect.NativeMethodAccessorImpl.invoke0(java.lang.reflect.Method, java.lang.Object, java.lang.Object[]) @bci=0 (Compiled frame)
 - sun.reflect.NativeMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) @bci=100, line=62 (Compiled frame)
 - sun.reflect.DelegatingMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) @bci=6, line=43 (Compiled frame)
 - java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) @bci=56, line=498 (Compiled frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.Attacher.install(java.lang.Class, java.lang.String, java.lang.String, boolean, java.lang.String) @bci=75, line=110 (Interpreted frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider, java.lang.String, java.lang.String, kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$AgentProvider, boolean) @bci=93, line=608 (Interpreted frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider, kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$ProcessProvider) @bci=24, line=586 (Interpreted frame)
 - kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent.install(kotlinx.coroutines.repackaged.net.bytebuddy.agent.ByteBuddyAgent$AttachmentProvider) @bci=4, line=538 (Interpreted frame)
 - kotlinx.coroutines.debug.internal.ByteBuddyDynamicAttach.attach() @bci=6, line=21 (Interpreted frame)
 - kotlinx.coroutines.debug.internal.ByteBuddyDynamicAttach.invoke(boolean) @bci=5, line=17 (Interpreted frame)
 - kotlinx.coroutines.debug.internal.ByteBuddyDynamicAttach.invoke(java.lang.Object) @bci=8, line=15 (Interpreted frame)
 - kotlinx.coroutines.debug.internal.DebugProbesImpl.install() @bci=154, line=85 (Interpreted frame)
 - kotlinx.coroutines.debug.DebugProbes.install() @bci=3, line=72 (Interpreted frame)
 - com.db.autobahn.alerts.base.ApplicationStarter.<clinit>() @bci=23, line=149 (Interpreted frame)
 - java.lang.Class.forName0(java.lang.String, boolean, java.lang.ClassLoader, java.lang.Class) @bci=0 (Compiled frame)
 - java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader) @bci=49, line=348 (Compiled frame)
 - org.springframework.cglib.core.ReflectUtils.defineClass(java.lang.String, byte[], java.lang.ClassLoader, java.security.ProtectionDomain, java.lang.Class) @bci=509, line=593 (Interpreted frame)
 - org.springframework.cglib.core.AbstractClassGenerator.generate(org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData) @bci=184, line=363 (Interpreted frame)
 - org.springframework.cglib.proxy.Enhancer.generate(org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData) @bci=53, line=585 (Interpreted frame)
 - org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(org.springframework.cglib.core.AbstractClassGenerator) @bci=5, line=110 (Interpreted frame)
 - org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(java.lang.Object) @bci=5, line=108 (Interpreted frame)
 - org.springframework.cglib.core.internal.LoadingCache$2.call() @bci=11, line=54 (Interpreted frame)
 - java.util.concurrent.FutureTask.run() @bci=42, line=266 (Interpreted frame)
 - org.springframework.cglib.core.internal.LoadingCache.createEntry(java.lang.Object, java.lang.Object, java.lang.Object) @bci=58, line=61 (Interpreted frame)
 - org.springframework.cglib.core.internal.LoadingCache.get(java.lang.Object) @bci=39, line=34 (Interpreted frame)
 - org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(org.springframework.cglib.core.AbstractClassGenerator, boolean) @bci=15, line=134 (Interpreted frame)
 - org.springframework.cglib.core.AbstractClassGenerator.create(java.lang.Object) @bci=115, line=319 (Interpreted frame)
 - org.springframework.cglib.proxy.Enhancer.createHelper() @bci=86, line=572 (Interpreted frame)
 - org.springframework.cglib.proxy.Enhancer.createClass() @bci=6, line=419 (Interpreted frame)
 - org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(org.springframework.cglib.proxy.Enhancer) @bci=1, line=137 (Interpreted frame)
 - org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(java.lang.Class, java.lang.ClassLoader) @bci=53, line=109 (Interpreted frame)
 - org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) @bci=461, line=447 (Interpreted frame)
 - org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) @bci=88, line=268 (Interpreted frame)
 - org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(java.util.Collection, org.springframework.beans.factory.config.ConfigurableListableBeanFactory) @bci=61, line=325 (Interpreted frame)
 - org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List) @bci=512, line=147 (Interpreted frame)
 - org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) @bci=5, line=746 (Interpreted frame)
 - org.springframework.context.support.AbstractApplicationContext.refresh() @bci=53, line=564 (Interpreted frame)
 - org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh() @bci=1, line=145 (Interpreted frame)
 - org.springframework.boot.SpringApplication.refresh(org.springframework.context.ConfigurableApplicationContext) @bci=1, line=730 (Interpreted frame)
 - org.springframework.boot.SpringApplication.refreshContext(org.springframework.context.ConfigurableApplicationContext) @bci=16, line=412 (Interpreted frame)
 - org.springframework.boot.SpringApplication.run(java.lang.String[]) @bci=107, line=302 (Interpreted frame)
 - org.springframework.boot.test.context.SpringBootContextLoader.loadContext(org.springframework.test.context.MergedContextConfiguration) @bci=260, line=121 (Interpreted frame)
 - org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(org.springframework.test.context.MergedContextConfiguration) @bci=27, line=99 (Interpreted frame)
 - org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(org.springframework.test.context.MergedContextConfiguration) @bci=24, line=124 (Interpreted frame)
 - org.springframework.test.context.support.DefaultTestContext.getApplicationContext() @bci=8, line=124 (Interpreted frame)
 - org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(org.springframework.test.context.TestContext) @bci=15, line=118 (Interpreted frame)
 - org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(org.springframework.test.context.TestContext) @bci=45, line=83 (Interpreted frame)
 - org.springframework.test.context.TestContextManager.prepareTestInstance(java.lang.Object) @bci=89, line=248 (Interpreted frame)
 - org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest() @bci=10, line=227 (Interpreted frame)
 - org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall() @bci=4, line=289 (Interpreted frame)
 - org.junit.internal.runners.model.ReflectiveCallable.run() @bci=1, line=12 (Interpreted frame)
 - org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(org.junit.runners.model.FrameworkMethod) @bci=8, line=291 (Interpreted frame)
 - org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(org.junit.runners.model.FrameworkMethod, org.junit.runner.notification.RunNotifier) @bci=24, line=246 (Interpreted frame)
 - org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(java.lang.Object, org.junit.runner.notification.RunNotifier) @bci=6, line=97 (Interpreted frame)
 - org.junit.runners.ParentRunner$3.run() @bci=12, line=290 (Interpreted frame)
 - org.junit.runners.ParentRunner$1.schedule(java.lang.Runnable) @bci=1, line=71 (Interpreted frame)
 - org.junit.runners.ParentRunner.runChildren(org.junit.runner.notification.RunNotifier) @bci=44, line=288 (Interpreted frame)
 - org.junit.runners.ParentRunner.access$000(org.junit.runners.ParentRunner, org.junit.runner.notification.RunNotifier) @bci=2, line=58 (Interpreted frame)
 - org.junit.runners.ParentRunner$2.evaluate() @bci=8, line=268 (Interpreted frame)
 - org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate() @bci=11, line=61 (Interpreted frame)
 - org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate() @bci=12, line=70 (Interpreted frame)
 - org.junit.runners.ParentRunner.run(org.junit.runner.notification.RunNotifier) @bci=20, line=363 (Interpreted frame)
 - org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(org.junit.runner.notification.RunNotifier) @bci=24, line=190 (Interpreted frame)
 - org.apache.maven.surefire.junit4.JUnit4Provider.execute(java.lang.Class, org.apache.maven.surefire.common.junit4.Notifier, org.junit.runner.manipulation.Filter) @bci=58, line=365 (Interpreted frame)
 - org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(java.lang.Class, org.apache.maven.surefire.common.junit4.Notifier) @bci=60, line=273 (Interpreted frame)
 - org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(java.lang.Class, org.apache.maven.surefire.report.RunListener, org.apache.maven.surefire.common.junit4.Notifier) @bci=34, line=238 (Interpreted frame)
 - org.apache.maven.surefire.junit4.JUnit4Provider.invoke(java.lang.Object) @bci=200, line=159 (Interpreted frame)
 - org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(org.apache.maven.surefire.booter.ForkingReporterFactory) @bci=9, line=379 (Interpreted frame)
 - org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess() @bci=7, line=340 (Interpreted frame)
 - org.apache.maven.surefire.booter.ForkedBooter.execute() @bci=1, line=125 (Interpreted frame)
 - org.apache.maven.surefire.booter.ForkedBooter.main(java.lang.String[]) @bci=35, line=413 (Interpreted frame)

@qwwdfsad
Copy link
Collaborator

qwwdfsad commented Jul 6, 2022

Thanks for the detailed report!

Also I am not sure if it will work well with multiple classloaders (we have some apps deployed as webapps into stand-alone Tomcat server)

Should work unless these classloaders all have loaded coroutines/stdlib that are trying to communicate directly (e.g. call each other). In that case, though, a debugger is the least of the problems :)

Could we create maven artifact in kotlinx.coroutines repo that can be placed at the beginning of the classpath to enable DebugProbes?

Unfortunately, the answer is no. The burden of maintaining it, explaining to users why it exists in the first place and what is the difference from other artifacts is just too high for the problem that is being solved.
We of course change this if there will be a lot of demand (e.g. hypothetical $newBuildSystemThatIsSuperPopular will require that), but not for the sake of a single use-case. I would suggest you stick with your own hand-rolled solution for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants