Skip to content

Commit cb40174

Browse files
jbachorikjpbempel
andauthored
Add probe diagnostics (#153)
Co-authored-by: Jean-Philippe Bempel <jean-philippe.bempel@datadoghq.com>
1 parent 4a9c924 commit cb40174

File tree

15 files changed

+306
-99
lines changed

15 files changed

+306
-99
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package datadog.trace.bootstrap.debugging;
2+
3+
import java.util.List;
4+
import lombok.extern.slf4j.Slf4j;
5+
6+
@Slf4j
7+
public class DebuggerContext {
8+
9+
public interface Sink {
10+
void addSnapshot(Snapshot snapshot);
11+
12+
void addDiagnostics(String probeId, List<DiagnosticMessage> messages);
13+
14+
default void skipSnapshot(Snapshot snapshot, long threshold, long duration) {}
15+
}
16+
17+
private static volatile Sink sink;
18+
19+
public static void init(Sink sink) {
20+
DebuggerContext.sink = sink;
21+
}
22+
23+
public static void skipSnapshot(Snapshot snapshot, long threshold, long duration) {
24+
Sink localSink = sink;
25+
if (localSink == null) {
26+
return;
27+
}
28+
log.info("SnapshotHelper.skip");
29+
localSink.skipSnapshot(snapshot, threshold, duration);
30+
}
31+
32+
public static void addSnapshot(Snapshot snapshot) {
33+
Sink localSink = sink;
34+
if (localSink == null) {
35+
return;
36+
}
37+
log.info("SnapshotHelper.add");
38+
localSink.addSnapshot(snapshot);
39+
}
40+
41+
public static void reportDiagnostics(String probeId, List<DiagnosticMessage> messages) {
42+
Sink localSink = sink;
43+
if (localSink == null) {
44+
return;
45+
}
46+
localSink.addDiagnostics(probeId, messages);
47+
}
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package datadog.trace.bootstrap.debugging;
2+
3+
import java.time.Instant;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.ToString;
6+
7+
@ToString
8+
@EqualsAndHashCode
9+
public final class DiagnosticMessage {
10+
public enum Kind {
11+
INFO,
12+
WARN,
13+
ERROR;
14+
}
15+
16+
private final long timestamp;
17+
private final Kind kind;
18+
private final String message;
19+
20+
public DiagnosticMessage(Kind kind, String message) {
21+
this.timestamp = Instant.now().toEpochMilli();
22+
this.kind = kind;
23+
this.message = message;
24+
}
25+
26+
public Kind getKind() {
27+
return kind;
28+
}
29+
30+
public String getMessage() {
31+
return message;
32+
}
33+
34+
public long getTimestamp() {
35+
return timestamp;
36+
}
37+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package datadog.trace.bootstrap.debugging;
2+
3+
import java.util.List;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.ToString;
6+
7+
@ToString
8+
@EqualsAndHashCode
9+
public final class ProbeDiagnostic {
10+
private final String probeId;
11+
private final List<DiagnosticMessage> messages;
12+
13+
public ProbeDiagnostic(String probeId, List<DiagnosticMessage> messages) {
14+
this.probeId = probeId;
15+
this.messages = messages;
16+
}
17+
18+
public String getProbeId() {
19+
return probeId;
20+
}
21+
22+
public List<DiagnosticMessage> getMessages() {
23+
return messages;
24+
}
25+
}

dd-java-agent/agent-debugging/debugging-bootstrap/src/main/java/datadog/trace/bootstrap/debugging/Snapshot.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ public final class Snapshot {
1515
private static final String LANGUAGE = "java";
1616
private static final int VERSION = 0;
1717

18-
public interface Listener {
19-
void onSkip(Snapshot snapshot, long threshold, long duration);
20-
21-
void onCommit(Snapshot snapshot);
22-
}
23-
2418
@ToString
2519
@EqualsAndHashCode
2620
public static final class Capture {
@@ -272,12 +266,12 @@ public void commit(long threshold, boolean lineScope) {
272266
duration = System.nanoTime() - startTs;
273267
if (threshold > -1 && !lineScope) {
274268
if (duration < threshold) {
275-
SnapshotHelper.skip(this, threshold, duration);
269+
DebuggerContext.skipSnapshot(this, threshold, duration);
276270
log.warn("Skipping snapshot");
277271
}
278272
}
279273
log.info("Committing snapshot: {}", this);
280-
SnapshotHelper.add(this);
274+
DebuggerContext.addSnapshot(this);
281275
}
282276

283277
public long getStartTs() {

dd-java-agent/agent-debugging/debugging-bootstrap/src/main/java/datadog/trace/bootstrap/debugging/SnapshotHelper.java

Lines changed: 0 additions & 37 deletions
This file was deleted.

dd-java-agent/agent-debugging/src/main/java/com/datadog/debugging/agent/DebuggerTransformer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.datadog.debugging.agent;
22

3+
import com.datadog.debugging.instrumentation.InstrumentationResult;
34
import com.datadog.debugging.instrumentation.MethodProbeInstrumentor;
45
import datadog.trace.agent.tooling.AgentTooling;
56
import datadog.trace.api.Config;
7+
import datadog.trace.bootstrap.debugging.DebuggerContext;
68
import java.io.IOException;
79
import java.lang.instrument.ClassFileTransformer;
810
import java.nio.file.Files;
@@ -63,8 +65,9 @@ public byte[] transform(
6365
if (where.isMethodMatching(methodNode.name)
6466
&& where.isSignatureMatching(methodNode.desc)) {
6567
log.debug("Instrumenting method: {}.{}{}", typeName, methodNode.name, methodNode.desc);
66-
MethodProbeInstrumentor.instrument(probe, methodNode);
67-
transformed = true;
68+
InstrumentationResult result = MethodProbeInstrumentor.instrument(probe, methodNode);
69+
transformed = result.isSuccess();
70+
DebuggerContext.reportDiagnostics(probe.getProbeId(), result.getDiagnostics());
6871
}
6972
}
7073
if (transformed) {

dd-java-agent/agent-debugging/src/main/java/com/datadog/debugging/agent/DebuggingAgent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.fasterxml.jackson.core.type.TypeReference;
44
import com.fasterxml.jackson.databind.ObjectMapper;
55
import datadog.trace.api.Config;
6-
import datadog.trace.bootstrap.debugging.SnapshotHelper;
6+
import datadog.trace.bootstrap.debugging.DebuggerContext;
77
import java.io.FileInputStream;
88
import java.io.IOException;
99
import java.io.InputStream;
@@ -17,7 +17,7 @@ public static synchronized void run(Instrumentation instrumentation) {
1717
if (Config.get().isDebuggingEnabled()) {
1818
log.info("starting debugging agent");
1919
SnapshotSink sink = new SnapshotSink(Config.get());
20-
SnapshotHelper.init(sink);
20+
DebuggerContext.init(sink);
2121
String configFileName = System.getProperty("dd.debugger.config-file", "debugger-config.json");
2222
try {
2323
DebuggerProbe[] probes = null;

dd-java-agent/agent-debugging/src/main/java/com/datadog/debugging/agent/SnapshotSink.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@
66
import com.fasterxml.jackson.core.JsonProcessingException;
77
import com.fasterxml.jackson.databind.ObjectMapper;
88
import datadog.trace.api.Config;
9-
import datadog.trace.bootstrap.debugging.SnapshotHelper;
9+
import datadog.trace.bootstrap.debugging.DebuggerContext;
10+
import datadog.trace.bootstrap.debugging.DiagnosticMessage;
11+
import datadog.trace.bootstrap.debugging.ProbeDiagnostic;
12+
import java.util.List;
1013
import lombok.extern.slf4j.Slf4j;
1114

1215
@Slf4j
13-
public class SnapshotSink implements SnapshotHelper.Sink {
16+
public class SnapshotSink implements DebuggerContext.Sink {
1417
private final SnapshotUploader snapshotUploader;
1518
private final boolean isJsonFormat;
19+
private static final ObjectMapper MAPPER = new ObjectMapper();
20+
21+
static {
22+
MAPPER.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
23+
}
1624

1725
public SnapshotSink(Config config) {
1826
this(config, new SnapshotUploader(config));
@@ -24,19 +32,36 @@ public SnapshotSink(Config config) {
2432
}
2533

2634
@Override
27-
public void add(datadog.trace.bootstrap.debugging.Snapshot snapshot) {
28-
// JSON or binary
35+
public void addSnapshot(datadog.trace.bootstrap.debugging.Snapshot snapshot) {
36+
if (isJsonFormat) {
37+
try {
38+
String jsonStr = MAPPER.writeValueAsString(snapshot);
39+
log.info("JsonPaylod: {}", jsonStr);
40+
snapshotUploader.upload(jsonStr, SnapshotUploader.Kind.SNAPSHOT);
41+
} catch (JsonProcessingException ex) {
42+
log.warn("Error during snapshot flush: ", ex);
43+
}
44+
} else {
45+
log.warn("Unsupported serialization format");
46+
// ByteBuffer buffer = ByteBuffer.allocate(4096); // FIXME
47+
// SnapshotWriter.write(snapshot, buffer);
48+
// snapshotUploader.upload(buffer);
49+
}
50+
}
51+
52+
@Override
53+
public void addDiagnostics(String probeId, List<DiagnosticMessage> messages) {
54+
ProbeDiagnostic probeDiagnostic = new ProbeDiagnostic(probeId, messages);
2955
if (isJsonFormat) {
3056
try {
31-
ObjectMapper mapper = new ObjectMapper();
32-
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
33-
String jsonStr = mapper.writeValueAsString(snapshot);
57+
String jsonStr = MAPPER.writeValueAsString(probeDiagnostic);
3458
log.info("JsonPaylod: {}", jsonStr);
35-
snapshotUploader.upload(jsonStr);
59+
snapshotUploader.upload(jsonStr, SnapshotUploader.Kind.DIAGNOSTIC);
3660
} catch (JsonProcessingException ex) {
37-
log.warn("Error during flush: ", ex);
61+
log.warn("Error during diagnostic flush: ", ex);
3862
}
3963
} else {
64+
log.warn("Unsupported serialization format");
4065
// ByteBuffer buffer = ByteBuffer.allocate(4096); // FIXME
4166
// SnapshotWriter.write(snapshot, buffer);
4267
// snapshotUploader.upload(buffer);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.datadog.debugging.instrumentation;
2+
3+
import datadog.trace.bootstrap.debugging.DiagnosticMessage;
4+
import java.util.List;
5+
6+
public class InstrumentationResult {
7+
private final boolean success;
8+
private final List<DiagnosticMessage> diagnostics;
9+
10+
public InstrumentationResult(boolean success, List<DiagnosticMessage> diagnostics) {
11+
this.success = success;
12+
this.diagnostics = diagnostics;
13+
}
14+
15+
public boolean isSuccess() {
16+
return success;
17+
}
18+
19+
public List<DiagnosticMessage> getDiagnostics() {
20+
return diagnostics;
21+
}
22+
}

dd-java-agent/agent-debugging/src/main/java/com/datadog/debugging/instrumentation/MethodProbeInstrumentor.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static com.datadog.debugging.instrumentation.Types.THROWABLE_TYPE;
1010

1111
import com.datadog.debugging.agent.DebuggerProbe;
12+
import datadog.trace.bootstrap.debugging.DiagnosticMessage;
1213
import datadog.trace.bootstrap.debugging.Snapshot;
1314
import java.util.ArrayList;
1415
import java.util.List;
@@ -56,20 +57,33 @@ LabelNode getLineLabel(int line) {
5657

5758
private final DebuggerProbe probe;
5859
private final MethodNode methodNode;
59-
private int snapshotVar = -1;
60-
private LabelNode returnHandlerLabel = null;
61-
private LabelNode snapshotInitLabel = new LabelNode();
60+
private final LabelNode snapshotInitLabel = new LabelNode();
6261

6362
private final LineMap lineMap = new LineMap();
6463
private final boolean isStatic;
65-
private int offset = 0;
6664
private final String[] argumentNames;
6765

68-
public static void instrument(DebuggerProbe probe, MethodNode methodNode) {
69-
new MethodProbeInstrumentor(probe, methodNode).instrument();
66+
private int offset = 0;
67+
private int snapshotVar = -1;
68+
private LabelNode returnHandlerLabel = null;
69+
70+
private final List<DiagnosticMessage> diagnostics;
71+
72+
public static InstrumentationResult instrument(DebuggerProbe probe, MethodNode methodNode) {
73+
List<DiagnosticMessage> diagnostics = new ArrayList<>();
74+
boolean success = true;
75+
try {
76+
new MethodProbeInstrumentor(probe, methodNode, diagnostics).instrument();
77+
} catch (Throwable t) {
78+
success = false;
79+
diagnostics.add(new DiagnosticMessage(DiagnosticMessage.Kind.ERROR, t.toString()));
80+
}
81+
return new InstrumentationResult(success, diagnostics);
7082
}
7183

72-
private MethodProbeInstrumentor(DebuggerProbe probe, MethodNode methodNode) {
84+
private MethodProbeInstrumentor(
85+
DebuggerProbe probe, MethodNode methodNode, List<DiagnosticMessage> diagnostics) {
86+
this.diagnostics = diagnostics;
7387
this.probe = probe;
7488
this.methodNode = methodNode;
7589

@@ -139,6 +153,8 @@ private void processInstruction(AbstractInsnNode node) {
139153
private void addLineCaptures(LineMap lineMap) {
140154
DebuggerProbe.SourceLine[] targetLines = probe.getWhere().getSourceLines();
141155
if (targetLines == null) {
156+
diagnostics.add(
157+
new DiagnosticMessage(DiagnosticMessage.Kind.WARN, "Missing line debug information"));
142158
return;
143159
}
144160
for (DebuggerProbe.SourceLine sourceLine : targetLines) {
@@ -338,6 +354,8 @@ private void tryBox(Type type, InsnList insnList) {
338354
private void collectLocalVariables(AbstractInsnNode location, InsnList insnList) {
339355
// expected stack top: [capture]
340356
if (location == null) {
357+
diagnostics.add(
358+
new DiagnosticMessage(DiagnosticMessage.Kind.WARN, "Missing local variable debug info"));
341359
// method capture, local variables are not initialized - bail out
342360
return;
343361
}

0 commit comments

Comments
 (0)