|
24 | 24 | import static utils.InstrumentationTestHelper.compileAndLoadClass; |
25 | 25 | import static utils.InstrumentationTestHelper.getLineForLineProbe; |
26 | 26 | import static utils.InstrumentationTestHelper.loadClass; |
| 27 | +import static utils.TestClassFileHelper.getClassFileBytes; |
27 | 28 | import static utils.TestHelper.getFixtureContent; |
28 | 29 | import static utils.TestHelper.setFieldInConfig; |
29 | 30 |
|
|
32 | 33 | import com.datadog.debugger.el.ValueScript; |
33 | 34 | import com.datadog.debugger.el.values.StringValue; |
34 | 35 | import com.datadog.debugger.instrumentation.InstrumentationResult; |
| 36 | +import com.datadog.debugger.instrumentation.Types; |
| 37 | +import com.datadog.debugger.probe.CodeOriginProbe; |
35 | 38 | import com.datadog.debugger.probe.LogProbe; |
36 | 39 | import com.datadog.debugger.probe.MetricProbe; |
37 | 40 | import com.datadog.debugger.probe.SpanDecorationProbe; |
@@ -3075,6 +3078,44 @@ public void methodParametersAttributeRecord() throws IOException, URISyntaxExcep |
3075 | 3078 | } |
3076 | 3079 | } |
3077 | 3080 |
|
| 3081 | + /* |
| 3082 | + * Regression test for: DatadogClassLoader attempted duplicate class definition for |
| 3083 | + * com.datadog.debugger.instrumentation.Types (LinkageError). |
| 3084 | + * |
| 3085 | + * When a CodeOriginProbe matches the agent's Types class (by FQN or simple name), and Types |
| 3086 | + * is being loaded for the first time by DatadogClassLoader, DebuggerTransformer.transform() is |
| 3087 | + * invoked as a ClassFileTransformer. Inside performInstrumentation(), CodeOriginInstrumenter |
| 3088 | + * accesses the static field Types.DEBUGGER_CONTEXT_TYPE, triggering a re-entrant loadClass() |
| 3089 | + * call for Types on the same thread. Because Java's synchronized is reentrant and Types is not |
| 3090 | + * yet registered in the JVM (defineClass hasn't completed), findLoadedClass() returns null and |
| 3091 | + * defineClass is called a second time, producing the LinkageError. |
| 3092 | + */ |
| 3093 | + @Test |
| 3094 | + public void noInstrumentationForAgentClasses() throws Exception { |
| 3095 | + // Install a CodeOriginProbe targeting the agent's Types class by FQN. |
| 3096 | + // This simulates a probe accidentally matching an agent class (e.g. via simple-name fallback |
| 3097 | + // in TransformerDefinitionMatcher when a user class is also named "Types"). |
| 3098 | + CodeOriginProbe probe = |
| 3099 | + new CodeOriginProbe( |
| 3100 | + PROBE_ID, |
| 3101 | + true, |
| 3102 | + Where.of("com.datadog.debugger.instrumentation.Types", "descriptorToSignature", null)); |
| 3103 | + installProbes(probe); |
| 3104 | + byte[] typeBytes = getClassFileBytes(Types.class); |
| 3105 | + // transform() proceeds to performInstrumentation(), which calls |
| 3106 | + // CodeOriginInstrumenter.codeOriginCall() → accesses Types.DEBUGGER_CONTEXT_TYPE. |
| 3107 | + // In production (when Types is not yet loaded), this re-enters DatadogClassLoader.loadClass() |
| 3108 | + // and triggers LinkageError: duplicate class definition for Types. |
| 3109 | + byte[] result = |
| 3110 | + currentTransformer.transform( |
| 3111 | + Types.class.getClassLoader(), |
| 3112 | + "com/datadog/debugger/instrumentation/Types", |
| 3113 | + null, |
| 3114 | + null, |
| 3115 | + typeBytes); |
| 3116 | + assertNull(result); |
| 3117 | + } |
| 3118 | + |
3078 | 3119 | private TestSnapshotListener setupInstrumentTheWorldTransformer( |
3079 | 3120 | String excludeFileName, String includeFileName) { |
3080 | 3121 | Config config = mock(Config.class); |
|
0 commit comments