1
1
package com .datadog .debugger .codeorigin ;
2
2
3
3
import static com .datadog .debugger .agent .ConfigurationAcceptor .Source .CODE_ORIGIN ;
4
+ import static java .util .Arrays .asList ;
5
+ import static java .util .Arrays .stream ;
4
6
5
7
import com .datadog .debugger .agent .ConfigurationUpdater ;
6
8
import com .datadog .debugger .exception .Fingerprinter ;
7
9
import com .datadog .debugger .probe .CodeOriginProbe ;
8
10
import com .datadog .debugger .probe .Where ;
11
+ import com .datadog .debugger .util .ClassFileLines ;
12
+ import com .datadog .debugger .util .ClassNameFiltering ;
9
13
import datadog .trace .api .Config ;
10
14
import datadog .trace .bootstrap .debugger .CapturedContext ;
11
15
import datadog .trace .bootstrap .debugger .DebuggerContext ;
15
19
import datadog .trace .bootstrap .instrumentation .api .AgentTracer ;
16
20
import datadog .trace .util .AgentTaskScheduler ;
17
21
import datadog .trace .util .stacktrace .StackWalkerFactory ;
22
+ import java .io .ByteArrayOutputStream ;
23
+ import java .io .IOException ;
24
+ import java .io .InputStream ;
18
25
import java .util .Collection ;
19
26
import java .util .Collections ;
20
27
import java .util .HashMap ;
28
+ import java .util .HashSet ;
29
+ import java .util .List ;
21
30
import java .util .Map ;
22
31
import java .util .UUID ;
23
32
import java .util .concurrent .ConcurrentHashMap ;
33
+ import java .util .stream .Collectors ;
34
+ import org .objectweb .asm .ClassReader ;
35
+ import org .objectweb .asm .tree .ClassNode ;
36
+ import org .objectweb .asm .tree .MethodNode ;
24
37
import org .slf4j .Logger ;
25
38
import org .slf4j .LoggerFactory ;
26
39
27
40
public class DefaultCodeOriginRecorder implements CodeOriginRecorder {
28
41
private static final Logger LOG = LoggerFactory .getLogger (DefaultCodeOriginRecorder .class );
29
42
30
- private final Config config ;
31
-
32
43
private final ConfigurationUpdater configurationUpdater ;
33
44
34
45
private final Map <String , CodeOriginProbe > fingerprints = new HashMap <>();
35
46
36
47
private final Map <String , CodeOriginProbe > probes = new ConcurrentHashMap <>();
37
48
38
- private final AgentTaskScheduler taskScheduler = AgentTaskScheduler .INSTANCE ;
49
+ private final AgentTaskScheduler taskScheduler ;
50
+
51
+ private final int maxUserFrames ;
52
+
53
+ // this really should only be used for testing
54
+ public DefaultCodeOriginRecorder () {
55
+ maxUserFrames = 8 ;
56
+ configurationUpdater = null ;
57
+ DebuggerContext .initClassNameFilter (
58
+ new ClassNameFiltering (
59
+ new HashSet <>(
60
+ asList (
61
+ "sun" ,
62
+ "org.junit" ,
63
+ "java." ,
64
+ "org.gradle" ,
65
+ "com.sun" ,
66
+ "worker.org.gradle" ,
67
+ "datadog" ,
68
+ "com.datadog.debugger.probe" ,
69
+ "com.datadog.debugger.codeorigin" ))));
70
+ new ClassNameFiltering (
71
+ new HashSet <>(
72
+ asList (
73
+ "sun" ,
74
+ "org.junit" ,
75
+ "java." ,
76
+ "org.gradle" ,
77
+ "com.sun" ,
78
+ "worker.org.gradle" ,
79
+ "datadog" ,
80
+ "com.datadog.debugger.probe" ,
81
+ "com.datadog.debugger.codeorigin" )));
82
+ taskScheduler = AgentTaskScheduler .INSTANCE ;
83
+ }
39
84
40
85
public DefaultCodeOriginRecorder (Config config , ConfigurationUpdater configurationUpdater ) {
41
- this .config = config ;
42
86
this .configurationUpdater = configurationUpdater ;
87
+ maxUserFrames = config .getDebuggerCodeOriginMaxUserFrames ();
88
+ taskScheduler = AgentTaskScheduler .INSTANCE ;
89
+ }
90
+
91
+ public DefaultCodeOriginRecorder (
92
+ Config config , ConfigurationUpdater configurationUpdater , AgentTaskScheduler taskScheduler ) {
93
+ this .configurationUpdater = configurationUpdater ;
94
+ maxUserFrames = config .getDebuggerCodeOriginMaxUserFrames ();
95
+ this .taskScheduler = taskScheduler ;
43
96
}
44
97
45
98
@ Override
@@ -66,7 +119,7 @@ public String captureCodeOrigin(String signature) {
66
119
new ProbeId (UUID .randomUUID ().toString (), 0 ),
67
120
where .getSignature (),
68
121
where ,
69
- config . getDebuggerCodeOriginMaxUserFrames () );
122
+ maxUserFrames );
70
123
addFingerprint (fingerprint , probe );
71
124
72
125
installProbe (probe );
@@ -84,6 +137,84 @@ public String captureCodeOrigin(String signature) {
84
137
return probe .getId ();
85
138
}
86
139
140
+ @ Override
141
+ public String captureCodeOrigin (
142
+ String name , Class <?> target , String method , Class <?>[] types , boolean entry ) {
143
+ String fingerprint = Fingerprinter .fingerprint (name );
144
+ if (fingerprint == null ) {
145
+ LOG .debug ("Unable to fingerprint stack trace" );
146
+ return null ;
147
+ }
148
+ CodeOriginProbe probe ;
149
+
150
+ if (isAlreadyInstrumented (fingerprint )) {
151
+ probe = fingerprints .get (fingerprint );
152
+ } else {
153
+ Where where =
154
+ Where .of (
155
+ target .getName (),
156
+ method ,
157
+ stream (types ).map (Class ::getTypeName ).collect (Collectors .joining (", " , "(" , ")" )));
158
+
159
+ probe =
160
+ new CodeOriginProbe (
161
+ new ProbeId (UUID .randomUUID ().toString (), 0 ),
162
+ where .getSignature (),
163
+ where ,
164
+ maxUserFrames );
165
+ addFingerprint (fingerprint , probe );
166
+
167
+ installProbe (probe );
168
+ // committing here manually so that first run probe encounters decorate the span until the
169
+ // instrumentation gets installed
170
+ probe .commit (
171
+ CapturedContext .EMPTY_CONTEXT , CapturedContext .EMPTY_CONTEXT , Collections .emptyList ());
172
+ }
173
+
174
+ return probe .getId ();
175
+ }
176
+
177
+ @ Override
178
+ public String captureCodeOrigin (AgentSpan span , boolean entry ) {
179
+ String fingerprint = Fingerprinter .fingerprint (span .getResourceName ());
180
+ if (fingerprint == null ) {
181
+ LOG .debug ("Unable to fingerprint stack trace" );
182
+ return null ;
183
+ }
184
+ CodeOriginProbe probe ;
185
+
186
+ if (isAlreadyInstrumented (fingerprint )) {
187
+ probe = fingerprints .get (fingerprint );
188
+ } else {
189
+ StackTraceElement element = findPlaceInStack ();
190
+ ClassNode classNode = parseClassFile (element .getClassName ());
191
+ ClassFileLines lines = new ClassFileLines (classNode );
192
+ List <MethodNode > methodsByLine = lines .getMethodsByLine (element .getLineNumber ());
193
+ Where where =
194
+ Where .of (
195
+ element .getClassName (),
196
+ element .getMethodName (),
197
+ "FIX ME" ,
198
+ String .valueOf (element .getLineNumber ()));
199
+
200
+ probe =
201
+ new CodeOriginProbe (
202
+ new ProbeId (UUID .randomUUID ().toString (), 0 ),
203
+ where .getSignature (),
204
+ where ,
205
+ maxUserFrames );
206
+ addFingerprint (fingerprint , probe );
207
+
208
+ installProbe (probe );
209
+ // committing here manually so that first run probe encounters decorate the span until the
210
+ // instrumentation gets installed
211
+ probe .commit (
212
+ CapturedContext .EMPTY_CONTEXT , CapturedContext .EMPTY_CONTEXT , Collections .emptyList ());
213
+ }
214
+
215
+ return probe .getId ();
216
+ }
217
+
87
218
private StackTraceElement findPlaceInStack () {
88
219
return StackWalkerFactory .INSTANCE .walk (
89
220
stream ->
@@ -104,7 +235,9 @@ void addFingerprint(String fingerprint, CodeOriginProbe probe) {
104
235
public String installProbe (CodeOriginProbe probe ) {
105
236
CodeOriginProbe installed = probes .putIfAbsent (probe .getId (), probe );
106
237
if (installed == null ) {
107
- taskScheduler .execute (() -> configurationUpdater .accept (CODE_ORIGIN , getProbes ()));
238
+ if (configurationUpdater != null ) {
239
+ taskScheduler .execute (() -> configurationUpdater .accept (CODE_ORIGIN , getProbes ()));
240
+ }
108
241
return probe .getId ();
109
242
}
110
243
return installed .getId ();
@@ -117,4 +250,24 @@ public CodeOriginProbe getProbe(String probeId) {
117
250
public Collection <CodeOriginProbe > getProbes () {
118
251
return probes .values ();
119
252
}
253
+
254
+ private ClassNode parseClassFile (String className ) {
255
+ byte [] bytes = new byte [8192 ];
256
+ try (InputStream inputStream =
257
+ getClass ()
258
+ .getClassLoader ()
259
+ .getResourceAsStream (String .format ("%s.class" , className .replace ('.' , '/' )))) {
260
+ ByteArrayOutputStream bao = new ByteArrayOutputStream ();
261
+ int bytesRead ;
262
+ while ((bytesRead = inputStream .read (bytes )) != -1 ) {
263
+ bao .write (bytes , 0 , bytesRead );
264
+ }
265
+ ClassNode classNode = new ClassNode ();
266
+ new ClassReader (bao .toByteArray ()).accept (classNode , ClassReader .SKIP_FRAMES );
267
+ return classNode ;
268
+ } catch (IOException e ) {
269
+ LOG .error ("Can't read class file information for {}" , className );
270
+ return null ;
271
+ }
272
+ }
120
273
}
0 commit comments