Как перехватить Kotlin Coroutines?
Я пытаюсь инструментировать сопрограммы Kotlin, аналогично тому, что здесь делается с помощью Javaagent. Я не хочу Javaagent.
Первый шаг - перехватить создание, приостановку и возобновление сопрограмм, определенных в DebugProbes. Код для этого следующий:
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.
1 ответ
Вы уверены, что класс еще не загружен? Попробуйте установить точку останова в
ClassInjector.UsingReflection
чтобы увидеть, действительно ли вы проходите через него, или инъекция прервана из-за ранее загруженного класса.
Более чистым решением был бы агент Java. Вы можете использовать byte-buddy-agent для его динамического создания
ByteBuddyAgent.install()
а затем зарегистрируйте
AgentBuilder
в теме.