11package com .datadog .debugger .agent ;
22
3+ import static com .datadog .debugger .instrumentation .ASMHelper .getLineNumbers ;
34import static java .util .Collections .singletonList ;
45import static java .util .stream .Collectors .toList ;
56
5253import java .util .Map ;
5354import java .util .Set ;
5455import java .util .concurrent .ConcurrentHashMap ;
56+ import java .util .function .BiConsumer ;
5557import java .util .regex .Pattern ;
5658import net .bytebuddy .description .type .TypeDescription ;
5759import net .bytebuddy .pool .TypePool ;
@@ -103,6 +105,7 @@ public class DebuggerTransformer implements ClassFileTransformer {
103105 private final Set <String > includeMethods ;
104106 private final Trie includeTrie ;
105107 private final Map <String , LogProbe > instrumentTheWorldProbes ;
108+ private final BiConsumer <MethodInfo , List <ProbeDefinition >> probeCreator ;
106109
107110 public interface InstrumentationListener {
108111 void instrumentationResult (ProbeDefinition definition , InstrumentationResult result );
@@ -119,7 +122,8 @@ public DebuggerTransformer(
119122 this .denyListHelper = new DenyListHelper (configuration .getDenyList ());
120123 this .listener = listener ;
121124 this .debuggerSink = debuggerSink ;
122- this .instrumentTheWorld = config .isDynamicInstrumentationInstrumentTheWorld ();
125+ String itwType = config .getDynamicInstrumentationInstrumentTheWorld ();
126+ this .instrumentTheWorld = itwType != null ;
123127 if (this .instrumentTheWorld ) {
124128 instrumentTheWorldProbes = new ConcurrentHashMap <>();
125129 excludeTrie = new Trie ();
@@ -138,6 +142,17 @@ public DebuggerTransformer(
138142 includeTrie ,
139143 includeClasses ,
140144 includeMethods );
145+ if (itwType .equals ("method" )) {
146+ probeCreator = this ::createMethodProbe ;
147+ } else if (itwType .equals ("line" )) {
148+ probeCreator = this ::createLineProbes ;
149+ } else {
150+ log .warn (
151+ "Invalid value for 'dd.debugger.instrument-the-world' property: {}. "
152+ + "Valid values are 'method' or 'line'." ,
153+ itwType );
154+ probeCreator = null ;
155+ }
141156 } else {
142157 instrumentTheWorldProbes = null ;
143158 excludeTrie = null ;
@@ -146,6 +161,7 @@ public DebuggerTransformer(
146161 includeTrie = null ;
147162 includeClasses = null ;
148163 includeMethods = null ;
164+ probeCreator = null ;
149165 }
150166 }
151167
@@ -211,8 +227,7 @@ public byte[] transform(
211227 ProtectionDomain protectionDomain ,
212228 byte [] classfileBuffer ) {
213229 if (instrumentTheWorld ) {
214- return transformTheWorld (
215- loader , classFilePath , classBeingRedefined , protectionDomain , classfileBuffer );
230+ return transformTheWorld (loader , classFilePath , protectionDomain , classfileBuffer );
216231 }
217232 if (skipInstrumentation (loader , classFilePath )) {
218233 return null ;
@@ -264,7 +279,6 @@ private boolean skipInstrumentation(ClassLoader loader, String classFilePath) {
264279 private byte [] transformTheWorld (
265280 ClassLoader loader ,
266281 String classFilePath ,
267- Class <?> classBeingRedefined ,
268282 ProtectionDomain protectionDomain ,
269283 byte [] classfileBuffer ) {
270284 try {
@@ -303,16 +317,11 @@ private byte[] transformTheWorld(
303317 }
304318 List <ProbeDefinition > probes = new ArrayList <>();
305319 Set <String > methodNames = new HashSet <>();
320+ ClassFileLines classFileLines = new ClassFileLines (classNode );
306321 for (MethodNode methodNode : classNode .methods ) {
307322 if (isMethodIncludedForTransformation (methodNode , classNode , methodNames )) {
308- LogProbe probe =
309- LogProbe .builder ()
310- .probeId (RandomUtils .randomUUID ().toString (), 0 )
311- .where (classNode .name , methodNode .name )
312- .captureSnapshot (false )
313- .build ();
314- probes .add (probe );
315- instrumentTheWorldProbes .put (probe .getProbeId ().getEncodedId (), probe );
323+ MethodInfo methodInfo = new MethodInfo (loader , classNode , methodNode , classFileLines );
324+ probeCreator .accept (methodInfo , probes );
316325 }
317326 }
318327 boolean transformed = performInstrumentation (loader , classFilePath , probes , classNode );
@@ -342,6 +351,39 @@ private boolean isMethodIncludedForTransformation(
342351 return methodNames .add (methodNode .name );
343352 }
344353
354+ private void createMethodProbe (MethodInfo methodInfo , List <ProbeDefinition > probes ) {
355+ LogProbe probe =
356+ LogProbe .builder ()
357+ .probeId (RandomUtils .randomUUID ().toString (), 0 )
358+ .where (methodInfo .getClassNode ().name , methodInfo .getMethodNode ().name )
359+ .captureSnapshot (false )
360+ .build ();
361+ probes .add (probe );
362+ instrumentTheWorldProbes .put (probe .getProbeId ().getEncodedId (), probe );
363+ }
364+
365+ private void createLineProbes (MethodInfo methodInfo , List <ProbeDefinition > probes ) {
366+ if (methodInfo .getMethodName ().equals ("<init>" )) {
367+ // skip constructor for now to avoid dealing with code before super calls
368+ return ;
369+ }
370+ if ((methodInfo .getMethodNode ().access & Opcodes .ACC_SYNTHETIC ) != 0 ) {
371+ // skip synthetic methods
372+ return ;
373+ }
374+ List <Integer > lineNumbers = getLineNumbers (methodInfo .getMethodNode ());
375+ for (Integer lineNumber : lineNumbers ) {
376+ LogProbe probe =
377+ LogProbe .builder ()
378+ .probeId (RandomUtils .randomUUID ().toString (), 0 )
379+ .where (methodInfo .getSourceFileName (), lineNumber )
380+ .captureSnapshot (false )
381+ .build ();
382+ probes .add (probe );
383+ instrumentTheWorldProbes .put (probe .getProbeId ().getEncodedId (), probe );
384+ }
385+ }
386+
345387 private boolean isClassLoaderRelated (ClassNode classNode ) {
346388 return classNode .superName .equals ("java/lang/ClassLoader" )
347389 || classNode .superName .equals ("java/net/URLClassLoader" )
0 commit comments