akarnokd / RxJavaExtensions

RxJava 2.x & 3.x extra sources, operators and components and ports of many 1.x companion libraries.
Apache License 2.0
686 stars 51 forks source link

Is it feasible to create a Java Agent similar to Reactor's Debug Agent? #153

Open dano opened 2 years ago

dano commented 2 years ago

I discovered that Project Reactor provides a Java Agent that does bytecode manipulation to do its equivalent of RxJava's assembly tracking (called "debug mode"), but without any runtime performance penalty. Naturally, this made me curious if RxJava could use a similar technique to get the benefit assembly tracking without high cost of generating stack traces. Does it seem possible, or are there differences in the underlying implementations of each that would make it infeasible/impossible for RxJava?

akarnokd commented 2 years ago

I don't know Project Reactor's Java Agent and I lack the experience to do one for RxJava. Feel free to implement one in your own library.

dano commented 2 years ago

Thanks, @akarnokd. It seems their Java Agent does two manipulations. One is to add call site info to every operator:

/**
 * Adds callSite info to every operator call (except "checkpoint").
 * Before:
 * <pre>
 *     Flux.just(1)
 *         .map(it -> it)
 * </pre>
 * After:
 * <pre>
 *     Flux flux = Hooks.addCallSiteInfo(Flux.just(1), "Flux.just -> MyClass.myMethod(MyClass.java:12)");
 *     flux = Hooks.addCallSiteInfo(flux.map(it -> it), "Flux.map -> MyClass.myMethod(MyClass.java:13)");
 * </pre>
 *
 */

The other is to "checkpoint" methods that return Reactor types but don't call operators:

/**
 * Checkpoints every method returning Mono/Flux/ParallelFlux,
 * unless there were operator calls in the method body.
 * Before:
 * <pre>
 *     public Mono<String> request() {
 *         return MyWebClient.get("/foo").bodyToMono(String.class);
 *     }
 * </pre>
 * After:
 * <pre>
 *     public Mono<String> request() {
 *         return MyWebClient.get("/foo").bodyToMono(String.class).checkpoint("MyClass.request(MyClass.java:123)");
 *     }
 * </pre>
 *
 */

I'm definitely way out of my comfort zone with both the RxJava/Reactor internals and with byte code manipulation, but it appears (though I could absolutely be missing something) like an RxJava equivalent could be made if the RxJava assembly tracking code was modified to replace RxJavaAssemblyException with a class that could generate its own stacktrace (as it does now), but could also take a String that specified a hard-coded stack location like in the code above. It'd then have to be injected into the *OnAssembly wrappers instead of being created in their constructors.

Anyway, not sure if I will get the time to dig into this further, but it does seem like it holds some promise as a way to debug RxJava code much more easily in production. If I get around to investigating more I will let you know what I find...