1

我正在尝试检测 Kotlin 协程,类似于这里使用 Javaagent 所做的。我不想要Javaagent。

第一步是拦截 DebugProbes 中定义的Coroutines的创建、暂停和恢复。代码如下:

public class Instrumentor {
    private static final Logger LOG = LoggerFactory.getLogger(Instrumentor.class);

    public static void install() {
        TypeDescription typeDescription = TypePool.Default.ofSystemLoader()
                .describe("kotlin.coroutines.jvm.internal.DebugProbesKt")
                .resolve();
        new ByteBuddy()
                .redefine(typeDescription, ClassFileLocator.ForClassLoader.ofSystemLoader())
                .method(ElementMatchers.named("probeCoroutineCreated").and(ElementMatchers.takesArguments(1)))
                .intercept(MethodDelegation.to(CoroutineCreatedAdvice.class))
                .method(ElementMatchers.named("probeCoroutineResumed").and(ElementMatchers.takesArguments(1)))
                .intercept(MethodDelegation.to(CoroutineResumedAdvice.class))
                .method(ElementMatchers.named("probeCoroutineSuspended").and(ElementMatchers.takesArguments(1)))
                .intercept(MethodDelegation.to(CoroutineSuspendedAdvice.class))
                .make()
                .load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION);

        DebugProbes.INSTANCE.install();
    }

    public static void uninstall() {
        DebugProbes.INSTANCE.uninstall();
    }

    public static class CoroutineCreatedAdvice {
        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
        public static Continuation<Object> exit(@Advice.Return(readOnly = false) Continuation<Object> retVal) {
            LOG.info("Coroutine created: {}", retVal);
           
            return retVal;
        }
    }

    public static class CoroutineResumedAdvice {
        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void enter(@Advice.Argument(0) final Continuation<Object> continuation) {
            LOG.info("Coroutine resumed: {}", continuation);
        }
    }

    public static class CoroutineSuspendedAdvice {
        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void enter(@Advice.Argument(0) final Continuation<Object> continuation) {
            LOG.info("Coroutine suspended: {}", continuation);
        }
    }
}

JUnit5测试触发拦截:

class CoroutineInstrumentationTest {
    companion object {
        @JvmStatic
        @BeforeAll
        fun beforeAll() {
            Instrumentor.install()
        }

        @JvmStatic
        @AfterAll
        fun afterAll() {
            Instrumentor.uninstall()
        }
    }

    @Test
    fun testInterception() {
        runBlocking {
            println("Test")
        }
    }
}

但是,不会发生拦截(通过没有日志语句和使用调试器来确认)。我是 Byte Buddy 的新手,所以我可能遗漏了一些东西。有任何想法吗?

Kotlin v1.4.10,Kotlin Coroutines v1.3.9,Byte Buddy v1.10.17。

4

1 回答 1

0

您确定此时尚未加载该类吗?尝试设置一个断点,ClassInjector.UsingReflection看看你是否真的通过了,或者注入是否由于先前加载的类而中止。

更清洁的解决方案是 Java 代理。您可以使用byte-buddy-agent动态创建一个ByteBuddyAgent.install(),然后AgentBuilder在其上注册一个。

于 2020-10-18T21:12:03.937 回答