Skip to content

Commit c96bc45

Browse files
committed
WIP
1 parent f19a626 commit c96bc45

File tree

12 files changed

+432
-68
lines changed

12 files changed

+432
-68
lines changed

dd-java-agent/agent-bootstrap/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies {
2020
api project(':dd-trace-api')
2121
api project(':internal-api')
2222
api project(':internal-api:internal-api-9')
23-
api project(':dd-java-agent:agent-logging')
23+
api project(':dd-java-agent:agent-debugger:debugger-bootstrap')
2424
api libs.slf4j
2525
// ^ Generally a bad idea for libraries, but we're shadowing.
2626

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/ServerDecorator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public abstract class ServerDecorator extends BaseDecorator {
1010
public AgentSpan afterStart(final AgentSpan span) {
1111
span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_SERVER);
1212
span.setTag(DDTags.LANGUAGE_TAG_KEY, DDTags.LANGUAGE_TAG_VALUE);
13+
// CodeOriginInfo.entry(span);
1314
return super.afterStart(span);
1415
}
1516
}

dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.time.temporal.ChronoUnit;
88
import java.util.ArrayList;
99
import java.util.List;
10+
import java.util.function.Function;
1011
import org.slf4j.Logger;
1112
import org.slf4j.LoggerFactory;
1213

@@ -73,6 +74,11 @@ public interface ExceptionDebugger {
7374

7475
public interface CodeOriginRecorder {
7576
String captureCodeOrigin(String signature);
77+
78+
String captureCodeOrigin(AgentSpan span, boolean entry);
79+
80+
String captureCodeOrigin(
81+
String name, Class<?> target, String method, Class<?>[] types, boolean entry);
7682
}
7783

7884
private static volatile ProbeResolver probeResolver;
@@ -363,6 +369,43 @@ public static String captureCodeOrigin(String signature) {
363369
return null;
364370
}
365371

372+
public static String captureCodeOrigin(Function<CodeOriginRecorder, String> function) {
373+
try {
374+
CodeOriginRecorder recorder = codeOriginRecorder;
375+
if (recorder != null) {
376+
return function.apply(recorder);
377+
}
378+
} catch (Exception ex) {
379+
LOGGER.debug("Error in captureCodeOrigin: ", ex);
380+
}
381+
return null;
382+
}
383+
384+
public static String captureCodeOrigin(AgentSpan span, boolean entry) {
385+
try {
386+
CodeOriginRecorder recorder = codeOriginRecorder;
387+
if (recorder != null) {
388+
return recorder.captureCodeOrigin(span, entry);
389+
}
390+
} catch (Exception ex) {
391+
LOGGER.debug("Error in captureCodeOrigin: ", ex);
392+
}
393+
return null;
394+
}
395+
396+
public static String captureCodeOrigin(
397+
String name, Class<?> target, String method, Class<?>[] types, boolean entry) {
398+
try {
399+
CodeOriginRecorder recorder = codeOriginRecorder;
400+
if (recorder != null) {
401+
return recorder.captureCodeOrigin(name, target, method, types, entry);
402+
}
403+
} catch (Exception ex) {
404+
LOGGER.debug("Error in captureCodeOrigin: ", ex);
405+
}
406+
return null;
407+
}
408+
366409
public static void handleException(Throwable t, AgentSpan span) {
367410
try {
368411
ExceptionDebugger exDebugger = exceptionDebugger;

dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/CodeOriginInfo.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,33 @@ public static void entry(Method method) {
1919
}
2020
}
2121

22+
public static void entry(Class<?> type, Method method) {
23+
if (InstrumenterConfig.get().isCodeOriginEnabled()) {
24+
String signature =
25+
stream(method.getParameterTypes())
26+
.map(Class::getTypeName)
27+
.collect(Collectors.joining(", ", "(", ")"));
28+
captureCodeOrigin(signature);
29+
}
30+
}
31+
32+
public static void entry(AgentSpan span) {
33+
if (InstrumenterConfig.get().isCodeOriginEnabled()) {
34+
captureCodeOrigin(span, true);
35+
}
36+
}
37+
38+
public static void entry(String name, Class<?> target, String method, Class<?>[] types) {
39+
if (InstrumenterConfig.get().isCodeOriginEnabled()) {
40+
captureCodeOrigin(name, target, method, types, true);
41+
}
42+
}
43+
2244
public static void exit(AgentSpan span) {
2345
if (InstrumenterConfig.get().isCodeOriginEnabled()) {
24-
String probeId = captureCodeOrigin(null);
46+
String probeId = captureCodeOrigin((String) null);
2547
if (span != null) {
26-
span.getLocalRootSpan().setTag(probeId, span);
48+
span.getLocalRootSpan().setTag(probeId, span.getSpanId());
2749
}
2850
}
2951
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java

Lines changed: 159 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.datadog.debugger.codeorigin;
22

33
import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.CODE_ORIGIN;
4+
import static java.util.Arrays.asList;
5+
import static java.util.Arrays.stream;
46

57
import com.datadog.debugger.agent.ConfigurationUpdater;
68
import com.datadog.debugger.exception.Fingerprinter;
79
import com.datadog.debugger.probe.CodeOriginProbe;
810
import com.datadog.debugger.probe.Where;
11+
import com.datadog.debugger.util.ClassFileLines;
12+
import com.datadog.debugger.util.ClassNameFiltering;
913
import datadog.trace.api.Config;
1014
import datadog.trace.bootstrap.debugger.CapturedContext;
1115
import datadog.trace.bootstrap.debugger.DebuggerContext;
@@ -15,31 +19,80 @@
1519
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
1620
import datadog.trace.util.AgentTaskScheduler;
1721
import datadog.trace.util.stacktrace.StackWalkerFactory;
22+
import java.io.ByteArrayOutputStream;
23+
import java.io.IOException;
24+
import java.io.InputStream;
1825
import java.util.Collection;
1926
import java.util.Collections;
2027
import java.util.HashMap;
28+
import java.util.HashSet;
29+
import java.util.List;
2130
import java.util.Map;
2231
import java.util.UUID;
2332
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;
2437
import org.slf4j.Logger;
2538
import org.slf4j.LoggerFactory;
2639

2740
public class DefaultCodeOriginRecorder implements CodeOriginRecorder {
2841
private static final Logger LOG = LoggerFactory.getLogger(DefaultCodeOriginRecorder.class);
2942

30-
private final Config config;
31-
3243
private final ConfigurationUpdater configurationUpdater;
3344

3445
private final Map<String, CodeOriginProbe> fingerprints = new HashMap<>();
3546

3647
private final Map<String, CodeOriginProbe> probes = new ConcurrentHashMap<>();
3748

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+
}
3984

4085
public DefaultCodeOriginRecorder(Config config, ConfigurationUpdater configurationUpdater) {
41-
this.config = config;
4286
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;
4396
}
4497

4598
@Override
@@ -66,7 +119,7 @@ public String captureCodeOrigin(String signature) {
66119
new ProbeId(UUID.randomUUID().toString(), 0),
67120
where.getSignature(),
68121
where,
69-
config.getDebuggerCodeOriginMaxUserFrames());
122+
maxUserFrames);
70123
addFingerprint(fingerprint, probe);
71124

72125
installProbe(probe);
@@ -84,6 +137,84 @@ public String captureCodeOrigin(String signature) {
84137
return probe.getId();
85138
}
86139

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+
87218
private StackTraceElement findPlaceInStack() {
88219
return StackWalkerFactory.INSTANCE.walk(
89220
stream ->
@@ -104,7 +235,9 @@ void addFingerprint(String fingerprint, CodeOriginProbe probe) {
104235
public String installProbe(CodeOriginProbe probe) {
105236
CodeOriginProbe installed = probes.putIfAbsent(probe.getId(), probe);
106237
if (installed == null) {
107-
taskScheduler.execute(() -> configurationUpdater.accept(CODE_ORIGIN, getProbes()));
238+
if (configurationUpdater != null) {
239+
taskScheduler.execute(() -> configurationUpdater.accept(CODE_ORIGIN, getProbes()));
240+
}
108241
return probe.getId();
109242
}
110243
return installed.getId();
@@ -117,4 +250,24 @@ public CodeOriginProbe getProbe(String probeId) {
117250
public Collection<CodeOriginProbe> getProbes() {
118251
return probes.values();
119252
}
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+
}
120273
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/Fingerprinter.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ public static String fingerprint(Throwable t, ClassNameFilter classNameFiltering
4242
return bytesToHex(digest.digest());
4343
}
4444

45+
public static String fingerprint(CharSequence resourceName) {
46+
try {
47+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
48+
digest.update(resourceName.toString().getBytes());
49+
return bytesToHex(digest.digest());
50+
} catch (NoSuchAlgorithmException e) {
51+
LOGGER.debug("Unable to find digest algorithm SHA-256", e);
52+
return null;
53+
}
54+
}
55+
4556
public static String fingerprint(StackTraceElement element) {
4657
try {
4758
MessageDigest digest = MessageDigest.getInstance("SHA-256");

0 commit comments

Comments
 (0)