-
Notifications
You must be signed in to change notification settings - Fork 586
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
Improve bytebuddy class enhance #521
Conversation
I will be back on this at May 3rd. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 main things should be considered
- Too many hijacking through reflection. This relies on byte-buddy internal codes/implementations. We don't want to make further upgrading hard. Please work with upstream and try to find a new way, or request byte-buddy to expose new APIs for this.
- As you are introducing a new way of
retransfer
, removing the old mechanism for the class cache is better because it isn't always perfect.
DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader, | ||
EnhanceContext context) throws PluginException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seem some local code style differences. Could you revert these changes?
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration() | ||
.to(BootstrapInstrumentBoost | ||
.forInternalDelegateClass(constructorInterceptPoint | ||
.getConstructorInterceptor())))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert all unnecessary changes.
@@ -161,9 +175,10 @@ public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder, | |||
final JavaModule javaModule, | |||
final ProtectionDomain protectionDomain) { | |||
LoadedLibraryCollector.registerURLClassLoader(classLoader); | |||
DelegateNamingResolver.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a little confused about this reset
. #transform
should be called for every type, as a static method, we reset every time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be a problem here, I'm not sure if there will be nested retransform multiple classes, which usually occurs the first time a class is loaded. If so, it would be unreasonable to reset all of them, just reset the counter of the class currently being transformed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If byte-buddy could expose the whole method signature rather than the name, I believe this would not be an issue.
How about asking for upstream first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? Tell me more about it.
Field nativeMethodStrategyField = clazz.getDeclaredField("nativeMethodStrategy"); | ||
nativeMethodStrategyField.setAccessible(true); | ||
nativeMethodStrategyField.set(agentBuilder, new SWNativeMethodStrategy(PREFIX)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seem a hijacking and relying on upstream(bytebuddy) doesn't change the internal implementation.
Considering bytebuddy is always very friendly to our community, you are better to submit a proposal/request to bytebuddy, and discuss the official API to do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, That's what I am thinking.
Field specialMethodInvocationField = auxiliaryTypeClass.getDeclaredField("specialMethodInvocation"); | ||
specialMethodInvocationField.setAccessible(true); | ||
SpecialMethodInvocation specialMethodInvocation = (SpecialMethodInvocation) specialMethodInvocationField.get(auxiliaryType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also another kind hijacking, please considering working with the upstream.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
|
||
} else if (auxiliaryTypeClassName.endsWith("Pipe$Binder$RedirectionProxy")) { | ||
// get MethodDescription from field 'sourceMethod' | ||
Field sourceMethodField = auxiliaryTypeClass.getDeclaredField("sourceMethod"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same hijacking concern.
|
||
} else if (auxiliaryTypeClassName.endsWith("FieldProxy$Binder$AccessorProxy")) { | ||
// get fieldDescription | ||
Field fieldDescriptionField = auxiliaryTypeClass.getDeclaredField("fieldDescription"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same hijacking concern.
import net.bytebuddy.utility.RandomString; | ||
|
||
/** | ||
* Design to generate fixed name for CacheValueField (cache delegating Method) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment doesn't fit the codes. Please reword this, you are building a ContextFactory with target type name based suffix on the top of Implementation.Context.Default
.
/** | ||
* Remove duplicated fields of instrumentedType | ||
*/ | ||
public class SWAsmVisitorWrapper implements AsmVisitorWrapper { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not fully following this. Are you trying to cache all original methods before the instrumentation?
The name RemoveDuplicatedFieldsClassVisitor
is not very clear to me.
Please reword the comments for SWAsmVisitorWrapper
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll check it agian.
Hi @kylixs Thanks for providing your way of debugging and resolving the issue. I believe locking the field and method name for auxiliary class and method is a way to resolve the transfer issue. |
It's a bit complicated, and we haven't tested enough scenarios to know if there are other compatibility issues. I came up with a few ideas:
|
Falling back usually is too complex to maintain. About the re-transfer, once we have a clear path about how skywalking agent works in that agent, I think we are fine. |
The key issue is the class and method that the plug-in intercepts. As you can see from the documentation of the process, the processing path is fairly clear, but it may not include all of the paths. Currently, all skywalking plug-ins are supported, but we don't know if any of the custom plug-ins have compatibility issues. At present, we have a better understanding of ByteBuddy processing process and debugging tools, which can solve the problem quickly. |
I don't quite understand this. Could you explain it in detail? |
The name of fields and methods are predictable and will be same. |
ByteBuddy uses random names, which is essential in dynamic proxy/class creation, but in APM scenarios, dynamic names are bad. |
I know they can, I mean many of them are not doing this. They don't change much, and customize many things. They just enhance some features, and release under their brand. |
If they haven't modified the Skywalking agent's plugin interceptor flow, they should be compatible. If they change the interceptor mechanism or the ByteBuddy option, they need to be compatible with the new mode. More information? Are there any known redistribution projects of Skywalking agent? |
I can't provide this information as I don't have their owner permission, sorry. |
Without enough information, it's hard to evaluate the results. Some possibilities:
|
Intercept same classes with two AgentBuilders are ok. Do you have any ideas on how to add specific test cases? |
What do you mean OK? We only have one, right? You changed it to multiple? Or one per plugin or something? |
Currently, it is supported to install an interceptor on the same method in two AgentBuilders respectively, resulting in a chain of proxy methods. I think there are a few scenarios where conflicts need to be resolved:
Interception methods can overlap in many ways, such as two plug-ins matching different annotations but having both annotations in the same class or method. I think it is better to solve the above problems in one agent, because putting it into two agents will bring maintenance workload and confusion. I think users would prefer to maintain one custom plug-in rather than two agents. One idea is to identify plug-ins that overlap and use different AgentBuilders to handle them. I'll try to see if this works. |
Generally, the ides works for me. |
Took some time to try two solutions:
I will submit it to the ByteBuddy community and need more discussion and design. ByteBuddy code: https://github.com/kylixs/byte-buddy/tree/separate-transformer |
I remembered that @raphw mentioned, the |
We can intercept the target class method using Advice, and then filter the matching plugins in the Advice static proxy method and invoke the intercepting classes of plugins. |
It is good to test various possibilities. Eventually, we should keep changes as less as possible, to keep stability. |
Sorry, but this does not go into a direction that I see being merged into Byte Buddy. But you are very welcome to implement this on top of existing APIs and to offer it as an extension. |
@kylixs I am back on this as the 9.5.0 had been released. AFAIK, there are several things scoped in this PR, which makes it very hard to review and move forward from tech and stable perspectives.
So, how about we are going to do <1> only first? I think from your existing changes, it should be easy to support that without hijacking? |
I agree with your idea, but I think <1> and <2> are the same things, and have been reached.
What has been tried:
Next step: |
That is fine.It can wait. |
I don't think we will avoid all hijackings, but we could limit the scope, currently, they are too wide. And we could add tests to verify them when we need to bump up the version of Byte-buddy. |
e4c3703
to
c6993f6
Compare
@kylixs Please open a new and clean changes PR. Let's focus on one at a time. |
move to: #561 |
Improve bytebuddy class enhance
[ x ] If this is non-trivial feature, paste the links/URLs to the design doc.
https://github.com/kylixs/kylixs.github.io/blob/master/skywalking/byteduddy-retransfrom-faliure-exploration.md
Update the documentation to include this new feature.
Tests(including UT, IT, E2E) are added to verify the new feature.
If it's UI related, attach the screenshots below.
If this pull request closes/resolves/fixes an existing issue, replace the issue number. Closes #.
Update the
CHANGES
log.