-
Notifications
You must be signed in to change notification settings - Fork 881
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
Make internal classloader instrumentation indy compatible #12242
Merged
trask
merged 31 commits into
open-telemetry:main
from
JonasKunz:indy-classloader-instrumentations
Oct 24, 2024
Merged
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
0fd0e09
Make internal classloader instrumentation indy compatible
JonasKunz d28efde
spotless
JonasKunz 18b9029
Added missing comment
JonasKunz 87f7d42
Checkstyle
JonasKunz 116daaa
Fix test relying on injection of Constants class
JonasKunz 106904e
Fix javadoc
JonasKunz f351043
Review fixes
JonasKunz e8a5ef2
Update instrumentation/internal/internal-class-loader/javaagent/src/m…
JonasKunz 67b1d40
Remove redundant excludes
JonasKunz 57d73cd
Replace copied bootstrap packages with access to BootstrapPackagesHolder
JonasKunz b46acad
Merge remote-tracking branch 'otel/HEAD' into indy-classloader-instru…
JonasKunz e54d881
Improved error output for bootstrapping recursion
JonasKunz 393e3ef
Test: Disable eager loading of advices and see if it fails anywhere
JonasKunz cd7c0f9
Test 2: Remove DefineClassInstrumentation and see if anything fails
JonasKunz 7fc8277
Revert "Test 2: Remove DefineClassInstrumentation and see if anything…
JonasKunz 33b6130
Revert "Test: Disable eager loading of advices and see if it fails an…
JonasKunz 2456716
Removed eager advice loading
JonasKunz dc26c83
Added support for nested invokedynamic bootstrapping using MutableCal…
JonasKunz 0154cd3
Added Unit test for DefineClassInstrumentation
JonasKunz 5f3f166
Fix problems with lambdas on recursive path
JonasKunz b01f387
Revert "Added Unit test for DefineClassInstrumentation"
JonasKunz 712c70d
spotless
JonasKunz 85beb7a
fix comments
JonasKunz 3e03153
Remove invalid package visibility comment
JonasKunz 05bc99f
Attempt to fix linkage errors
JonasKunz 492c968
Merge remote-tracking branch 'otel/HEAD' into indy-classloader-instru…
JonasKunz 6662850
Added pre-cautionary catch of linkage errors
JonasKunz 1173824
Revert changes not needed anymore after self-review
JonasKunz 43a4ae0
Revert unnecessary changes to AgentClassLoader
JonasKunz 03932e4
Typo fix
JonasKunz add664a
Update javaagent-tooling/src/main/java/io/opentelemetry/javaagent/too…
trask File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
...in/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/AdviceBootstrapState.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.tooling.instrumentation.indy; | ||
|
||
import java.lang.invoke.MutableCallSite; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.function.Supplier; | ||
import javax.annotation.Nullable; | ||
|
||
class AdviceBootstrapState implements AutoCloseable { | ||
|
||
private static final ThreadLocal<Map<Key, AdviceBootstrapState>> stateForCurrentThread = | ||
ThreadLocal.withInitial(HashMap::new); | ||
|
||
private final Key key; | ||
private int recursionDepth; | ||
@Nullable private MutableCallSite nestedCallSite; | ||
|
||
/** | ||
* We have to eagerly initialize to not cause a lambda construction during {@link #enter(Class, | ||
* String, String, String, String)}. | ||
*/ | ||
private static final Function<Key, AdviceBootstrapState> CONSTRUCTOR = AdviceBootstrapState::new; | ||
|
||
private AdviceBootstrapState(Key key) { | ||
this.key = key; | ||
// enter will increment it by one, so 0 is the value for non-recursive calls | ||
recursionDepth = -1; | ||
} | ||
|
||
static void initialize() { | ||
// Eager initialize everything because we could run into recursions doing this during advice | ||
// bootstrapping | ||
stateForCurrentThread.get(); | ||
stateForCurrentThread.remove(); | ||
try { | ||
Class.forName(Key.class.getName()); | ||
} catch (ClassNotFoundException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
|
||
static AdviceBootstrapState enter( | ||
Class<?> instrumentedClass, | ||
String moduleClassName, | ||
String adviceClassName, | ||
String adviceMethodName, | ||
String adviceMethodDescriptor) { | ||
Key key = | ||
new Key( | ||
instrumentedClass, | ||
moduleClassName, | ||
adviceClassName, | ||
adviceMethodName, | ||
adviceMethodDescriptor); | ||
AdviceBootstrapState state = stateForCurrentThread.get().computeIfAbsent(key, CONSTRUCTOR); | ||
state.recursionDepth++; | ||
return state; | ||
} | ||
|
||
public boolean isNestedInvocation() { | ||
return recursionDepth > 0; | ||
} | ||
|
||
public MutableCallSite getOrInitMutableCallSite(Supplier<MutableCallSite> initializer) { | ||
if (nestedCallSite == null) { | ||
nestedCallSite = initializer.get(); | ||
} | ||
return nestedCallSite; | ||
} | ||
|
||
public void initMutableCallSite(MutableCallSite mutableCallSite) { | ||
if (nestedCallSite != null) { | ||
throw new IllegalStateException("callsite has already been initialized"); | ||
} | ||
nestedCallSite = mutableCallSite; | ||
} | ||
|
||
@Nullable | ||
public MutableCallSite getMutableCallSite() { | ||
return nestedCallSite; | ||
} | ||
|
||
@Override | ||
public void close() { | ||
if (recursionDepth == 0) { | ||
Map<Key, AdviceBootstrapState> stateMap = stateForCurrentThread.get(); | ||
stateMap.remove(key); | ||
if (stateMap.isEmpty()) { | ||
// Do not leave an empty map dangling as thread local | ||
stateForCurrentThread.remove(); | ||
} | ||
} else { | ||
recursionDepth--; | ||
} | ||
} | ||
|
||
/** Key uniquely identifying a single invokedynamic instruction inserted for an advice */ | ||
private static class Key { | ||
|
||
private final Class<?> instrumentedClass; | ||
private final String moduleClassName; | ||
private final String adviceClassName; | ||
private final String adviceMethodName; | ||
private final String adviceMethodDescriptor; | ||
|
||
private Key( | ||
Class<?> instrumentedClass, | ||
String moduleClassName, | ||
String adviceClassName, | ||
String adviceMethodName, | ||
String adviceMethodDescriptor) { | ||
this.instrumentedClass = instrumentedClass; | ||
this.moduleClassName = moduleClassName; | ||
this.adviceClassName = adviceClassName; | ||
this.adviceMethodName = adviceMethodName; | ||
this.adviceMethodDescriptor = adviceMethodDescriptor; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || !(o instanceof Key)) { | ||
return false; | ||
} | ||
|
||
Key that = (Key) o; | ||
return instrumentedClass.equals(that.instrumentedClass) | ||
&& moduleClassName.equals(that.moduleClassName) | ||
&& adviceClassName.equals(that.adviceClassName) | ||
&& adviceMethodName.equals(that.adviceMethodName) | ||
&& adviceMethodDescriptor.equals(that.adviceMethodDescriptor); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
int result = instrumentedClass.hashCode(); | ||
result = 31 * result + moduleClassName.hashCode(); | ||
result = 31 * result + adviceClassName.hashCode(); | ||
result = 31 * result + adviceMethodName.hashCode(); | ||
result = 31 * result + adviceMethodDescriptor.hashCode(); | ||
return result; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,6 +67,9 @@ static byte[] transform(byte[] bytes) { | |
})); | ||
|
||
TransformationContext context = new TransformationContext(); | ||
if (justDelegateAdvice) { | ||
context.disableReturnTypeChange(); | ||
} | ||
Comment on lines
+70
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [for reviewer] without this the advice return type is assumed to be an array, which means |
||
ClassVisitor cv = | ||
new ClassVisitor(AsmApi.VERSION, cw) { | ||
|
||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Not related to this PR but I think we need to add a method to the experimental interface that lets instrumentation module declare that it is compatible with non-inline advice so we could disable the advice rewriting for it.
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 was thinking of it but felt like this instrumentation was a special case where this was needed and that would only be temporary until we remove support for non-indy instrumentations.
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.
Actually I was thinking that the main use for it would be to track the indy conversion progress and ensuring that modules that were made indy compatible once aren't broken by later changes. I'm pretty sure there was at least one more module that use the same trick to disable the advice rewriting.