52
52
import java .util .Map ;
53
53
import java .util .Set ;
54
54
import java .util .concurrent .ConcurrentHashMap ;
55
+ import java .util .function .BiConsumer ;
55
56
import java .util .regex .Pattern ;
56
57
import net .bytebuddy .description .type .TypeDescription ;
57
58
import net .bytebuddy .pool .TypePool ;
@@ -103,6 +104,7 @@ public class DebuggerTransformer implements ClassFileTransformer {
103
104
private final Set <String > includeMethods ;
104
105
private final Trie includeTrie ;
105
106
private final Map <String , LogProbe > instrumentTheWorldProbes ;
107
+ private final BiConsumer <MethodInfo , List <ProbeDefinition >> probeCreator ;
106
108
107
109
public interface InstrumentationListener {
108
110
void instrumentationResult (ProbeDefinition definition , InstrumentationResult result );
@@ -119,7 +121,8 @@ public DebuggerTransformer(
119
121
this .denyListHelper = new DenyListHelper (configuration .getDenyList ());
120
122
this .listener = listener ;
121
123
this .debuggerSink = debuggerSink ;
122
- this .instrumentTheWorld = config .isDynamicInstrumentationInstrumentTheWorld ();
124
+ String itwType = config .getDynamicInstrumentationInstrumentTheWorld ();
125
+ this .instrumentTheWorld = itwType != null ;
123
126
if (this .instrumentTheWorld ) {
124
127
instrumentTheWorldProbes = new ConcurrentHashMap <>();
125
128
excludeTrie = new Trie ();
@@ -138,6 +141,17 @@ public DebuggerTransformer(
138
141
includeTrie ,
139
142
includeClasses ,
140
143
includeMethods );
144
+ if (itwType .equals ("method" )) {
145
+ probeCreator = this ::createMethodProbe ;
146
+ } else if (itwType .equals ("line" )) {
147
+ probeCreator = this ::createLineProbes ;
148
+ } else {
149
+ log .warn (
150
+ "Invalid value for 'dd.debugger.instrument-the-world' property: {}. "
151
+ + "Valid values are 'method' or 'line'." ,
152
+ itwType );
153
+ probeCreator = null ;
154
+ }
141
155
} else {
142
156
instrumentTheWorldProbes = null ;
143
157
excludeTrie = null ;
@@ -146,6 +160,7 @@ public DebuggerTransformer(
146
160
includeTrie = null ;
147
161
includeClasses = null ;
148
162
includeMethods = null ;
163
+ probeCreator = null ;
149
164
}
150
165
}
151
166
@@ -211,8 +226,7 @@ public byte[] transform(
211
226
ProtectionDomain protectionDomain ,
212
227
byte [] classfileBuffer ) {
213
228
if (instrumentTheWorld ) {
214
- return transformTheWorld (
215
- loader , classFilePath , classBeingRedefined , protectionDomain , classfileBuffer );
229
+ return transformTheWorld (loader , classFilePath , protectionDomain , classfileBuffer );
216
230
}
217
231
if (skipInstrumentation (loader , classFilePath )) {
218
232
return null ;
@@ -264,7 +278,6 @@ private boolean skipInstrumentation(ClassLoader loader, String classFilePath) {
264
278
private byte [] transformTheWorld (
265
279
ClassLoader loader ,
266
280
String classFilePath ,
267
- Class <?> classBeingRedefined ,
268
281
ProtectionDomain protectionDomain ,
269
282
byte [] classfileBuffer ) {
270
283
try {
@@ -303,16 +316,11 @@ private byte[] transformTheWorld(
303
316
}
304
317
List <ProbeDefinition > probes = new ArrayList <>();
305
318
Set <String > methodNames = new HashSet <>();
319
+ ClassFileLines classFileLines = new ClassFileLines (classNode );
306
320
for (MethodNode methodNode : classNode .methods ) {
307
321
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 );
322
+ MethodInfo methodInfo = new MethodInfo (loader , classNode , methodNode , classFileLines );
323
+ probeCreator .accept (methodInfo , probes );
316
324
}
317
325
}
318
326
boolean transformed = performInstrumentation (loader , classFilePath , probes , classNode );
@@ -342,6 +350,39 @@ private boolean isMethodIncludedForTransformation(
342
350
return methodNames .add (methodNode .name );
343
351
}
344
352
353
+ private void createMethodProbe (MethodInfo methodInfo , List <ProbeDefinition > probes ) {
354
+ LogProbe probe =
355
+ LogProbe .builder ()
356
+ .probeId (RandomUtils .randomUUID ().toString (), 0 )
357
+ .where (methodInfo .getClassNode ().name , methodInfo .getMethodNode ().name )
358
+ .captureSnapshot (false )
359
+ .build ();
360
+ probes .add (probe );
361
+ instrumentTheWorldProbes .put (probe .getProbeId ().getEncodedId (), probe );
362
+ }
363
+
364
+ private void createLineProbes (MethodInfo methodInfo , List <ProbeDefinition > probes ) {
365
+ if (methodInfo .getMethodName ().equals ("<init>" )) {
366
+ // skip constructor for now to avoid dealing with code before super calls
367
+ return ;
368
+ }
369
+ if ((methodInfo .getMethodNode ().access & Opcodes .ACC_SYNTHETIC ) != 0 ) {
370
+ // skip synthetic methods
371
+ return ;
372
+ }
373
+ List <Integer > lineNumbers = methodInfo .getLineNumbers ();
374
+ for (Integer lineNumber : lineNumbers ) {
375
+ LogProbe probe =
376
+ LogProbe .builder ()
377
+ .probeId (RandomUtils .randomUUID ().toString (), 0 )
378
+ .where (methodInfo .getSourceFileName (), lineNumber )
379
+ .captureSnapshot (false )
380
+ .build ();
381
+ probes .add (probe );
382
+ instrumentTheWorldProbes .put (probe .getProbeId ().getEncodedId (), probe );
383
+ }
384
+ }
385
+
345
386
private boolean isClassLoaderRelated (ClassNode classNode ) {
346
387
return classNode .superName .equals ("java/lang/ClassLoader" )
347
388
|| classNode .superName .equals ("java/net/URLClassLoader" )
0 commit comments