2121import datadog .trace .bootstrap .debugger .ProbeRateLimiter ;
2222import datadog .trace .relocate .api .RatelimitedLogger ;
2323import datadog .trace .util .TagsHelper ;
24+ import java .lang .annotation .Annotation ;
25+ import java .lang .annotation .ElementType ;
26+ import java .lang .annotation .Target ;
2427import java .lang .instrument .Instrumentation ;
28+ import java .lang .reflect .AnnotatedType ;
29+ import java .lang .reflect .Array ;
2530import java .lang .reflect .Method ;
31+ import java .lang .reflect .Modifier ;
2632import java .lang .reflect .Parameter ;
2733import java .util .ArrayList ;
34+ import java .util .Arrays ;
2835import java .util .Collection ;
2936import java .util .Collections ;
3037import java .util .EnumMap ;
4451 * re-transformation of required classes
4552 */
4653public class ConfigurationUpdater implements DebuggerContext .ProbeResolver , ConfigurationAcceptor {
47-
54+ private static final Logger LOGGER = LoggerFactory .getLogger (ConfigurationUpdater .class );
55+ private static final int MINUTES_BETWEEN_ERROR_LOG = 5 ;
4856 private static final boolean JAVA_AT_LEAST_19 = JavaVirtualMachine .isJavaVersionAtLeast (19 );
57+ private static final boolean JAVA_AT_LEAST_16 = JavaVirtualMachine .isJavaVersionAtLeast (16 );
58+ private static final Method GET_RECORD_COMPONENTS_METHOD ;
59+ private static final Method GET_ANNOTATED_TYPES_METHOD ;
60+
61+ static {
62+ Method getRecordComponentsMethod = null ;
63+ Method getAnnotatedTypesMethod = null ;
64+ if (JAVA_AT_LEAST_16 ) {
65+ try {
66+ Class <?> recordClass = Class .forName ("java.lang.Record" , true , null );
67+ getRecordComponentsMethod = recordClass .getClass ().getDeclaredMethod ("getRecordComponents" );
68+ Class <?> recordComponentClass =
69+ Class .forName ("java.lang.reflect.RecordComponent" , true , null );
70+ getAnnotatedTypesMethod = recordComponentClass .getDeclaredMethod ("getAnnotatedType" );
71+ } catch (Exception e ) {
72+ LOGGER .debug ("Exception initializing reflection constants" , e );
73+ }
74+ }
75+ GET_RECORD_COMPONENTS_METHOD = getRecordComponentsMethod ;
76+ GET_ANNOTATED_TYPES_METHOD = getAnnotatedTypesMethod ;
77+ }
4978
5079 public interface TransformerSupplier {
5180 DebuggerTransformer supply (
@@ -56,9 +85,6 @@ DebuggerTransformer supply(
5685 DebuggerSink debuggerSink );
5786 }
5887
59- private static final Logger LOGGER = LoggerFactory .getLogger (ConfigurationUpdater .class );
60- private static final int MINUTES_BETWEEN_ERROR_LOG = 5 ;
61-
6288 private final Instrumentation instrumentation ;
6389 private final TransformerSupplier transformerSupplier ;
6490 private final Lock configurationLock = new ReentrantLock ();
@@ -185,6 +211,7 @@ private void handleProbesChanges(ConfigurationComparer changes, Configuration ne
185211 List <Class <?>> changedClasses =
186212 finder .getAllLoadedChangedClasses (instrumentation .getAllLoadedClasses (), changes );
187213 changedClasses = detectMethodParameters (changes , changedClasses );
214+ changedClasses = detectRecordWithTypeAnnotation (changes , changedClasses );
188215 retransformClasses (changedClasses );
189216 // ensures that we have at least re-transformed 1 class
190217 if (changedClasses .size () > 0 ) {
@@ -248,6 +275,65 @@ private List<Class<?>> detectMethodParameters(
248275 return result ;
249276 }
250277
278+ private List <Class <?>> detectRecordWithTypeAnnotation (
279+ ConfigurationComparer changes , List <Class <?>> changedClasses ) {
280+ if (!JAVA_AT_LEAST_16 ) {
281+ // records introduced in JDK 16 (final version)
282+ return changedClasses ;
283+ }
284+ List <Class <?>> result = new ArrayList <>();
285+ for (Class <?> changedClass : changedClasses ) {
286+ boolean addClass = true ;
287+ try {
288+ if (changedClass .getSuperclass ().getTypeName ().equals ("java.lang.Record" )
289+ && Modifier .isFinal (changedClass .getModifiers ())) {
290+ if (hasTypeAnnotationOnRecordComponent (changedClass )) {
291+ LOGGER .debug (
292+ "Record with type annotation detected, instrumentation not supported for {}" ,
293+ changedClass .getTypeName ());
294+ reportError (
295+ changes ,
296+ "Record with type annotation detected, instrumentation not supported for "
297+ + changedClass .getTypeName ());
298+ addClass = false ;
299+ }
300+ }
301+ } catch (Exception e ) {
302+ LOGGER .debug ("Exception detecting record with type annotation" , e );
303+ }
304+ if (addClass ) {
305+ result .add (changedClass );
306+ }
307+ }
308+ return result ;
309+ }
310+
311+ private boolean hasTypeAnnotationOnRecordComponent (Class <?> recordClass ) {
312+ if (GET_RECORD_COMPONENTS_METHOD == null || GET_ANNOTATED_TYPES_METHOD == null ) {
313+ return false ;
314+ }
315+ try {
316+ Object recordComponentsArray = GET_RECORD_COMPONENTS_METHOD .invoke (recordClass );
317+ int len = Array .getLength (recordComponentsArray );
318+ for (int i = 0 ; i < len ; i ++) {
319+ Object recordComponent = Array .get (recordComponentsArray , i );
320+ AnnotatedType annotatedType =
321+ (AnnotatedType ) GET_ANNOTATED_TYPES_METHOD .invoke (recordComponent );
322+ for (Annotation annotation : annotatedType .getAnnotations ()) {
323+ Target annotationTarget = annotation .annotationType ().getAnnotation (Target .class );
324+ if (annotationTarget != null
325+ && Arrays .stream (annotationTarget .value ())
326+ .anyMatch (it -> it == ElementType .TYPE_USE )) {
327+ return true ;
328+ }
329+ }
330+ }
331+ return false ;
332+ } catch (Exception ex ) {
333+ return false ;
334+ }
335+ }
336+
251337 private void reportReceived (ConfigurationComparer changes ) {
252338 for (ProbeDefinition def : changes .getAddedDefinitions ()) {
253339 if (def instanceof ExceptionProbe ) {
0 commit comments