java-agent
java-agent copied to clipboard
Rules for the java-redis-client integration
I'm looking into this and found the Lettuce client very easy to provide rules for. However, the Jedis client uses inheritance instead of composition so I am finding it more difficult to figure out the proper Byteman rules for it.
It would be simple if we could simply intercept the call to the constructor for Jedis and replace it with TracingJedis by using AT INVOKE Jedis.<init>, but this doesn't appear to help in the general case since we have to explicitly state the triggering class/method (no wildcards).
What about doing a PR to java-redis-client to change TracingJedis to be a wrapper? It could still support all of the current constructors for backward compatibility, but internally just wrap an instance of Jedis?
Agree about intercepting calls to the constructor - could have used that solution for the java-concurrent lib. Not sure if there is anything that ByteMan could do to help this situation? cc @adinn (when back from PTO)
I started down that path of refactoring the OT instrumentation for Jedis but then I realized that even if that wraps the Jedis client, the same problem exists -- intercepting the class construction and wrapping it without a generic way to intercept class instantiation in any possible triggering method. For the Lettuce client it is simple because all instances have to get created through a builder, but Jedis doesn't have builders.
I'll admit I don't really understand why ByteMan has this limitation. Is it intrinsic, or something the ByteMan project chooses as a design constraint?
There was a feature request to instrument instance creation itself (and presumably not just the constructor function) a while back. I've been studying the underlying ASM library to determine how feasible it is to do.
However, even if we could intercept and swap out/wrap the instance creation result, I think it would still have to be done in the context of a specific class+method, which precludes a generic rule for it without some kind of wildcard support on those fields.
Hi Ted/Ben,
I'll admit I don't really understand why ByteMan has this limitation. Is it intrinsic, or something the ByteMan project chooses as a design constraint?
However, even if we could intercept and swap out/wrap the instance creation result, I think it would still have to be done in the context of a specific class+method, which precludes a generic rule for it without some kind of wildcard support on those fields.
The proposal for an AFTER NEW|NEWARRAY|NEWMULTIARRAY location for rule injection does indeed provide an opportunity for rules to intercept object create operations in a way that differs from injection into a constructor. In particular, it would be possible for the rule to be passed the object created by the new operation and update it with a type-compatible alternative (i.e. a different object of the same type or subtype). However ...
Whatever might get implemented would still have to be limited to modifying bytecode of specific methods from specific classes at the point where a new operation occurs -- well, to be utterly precise, at the point where the new and associated constructor invocation have both completed (for reasons of type safety these two operations must remain indivisible). If you want a rule that injects code at every new operation for some given type without limiting the injection scope to a specific target method and class then Byteman is not the tool for you. The only 'looseness' that Byteman allowsfor in the definition of the rule target is via the use of interface rules, overriding rules and rules that omit a method signature and, hence, may apply to overloaded methods. That is indeed a long-standing policy decision.
The reason for this policy is not that it is impossible to implement, rather that online checking of every method of every loaded class for the presence of a potential injection point utterly kills performance. It's just not possible to make this innocuous. The same problem killed JBoss AOP's online transformation mode and has been seriously detrimental to use of many other injection-based tools/frameworks. If you need that type of 'wild-card' injection then there are two much better ways to do it:
-
Transform the source code or bytecode for the classes you wish to transform offline
-
Load and scan the classes you wish to transform in an auxiliary JVM or an auxiliary thread within the current JVM and generate per class rules to inject the side effects you want.
Neither of these is always easy to achieve but they both bypass the enormous performance hit that arises from having to scan every single loaded method.