Skip to content

Commit 734e3c5

Browse files
authored
Merge pull request #6868 from DataDog/jpbempel/stack-depth-assignment
Use stack depth to do snapshot assignment
2 parents 386364a + 34c20c0 commit 734e3c5

9 files changed

Lines changed: 294 additions & 101 deletions

File tree

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,15 @@ private static void processSnapshotsAndSetTags(
9191
}
9292
int[] mapping = createThrowableMapping(innerMostException, t);
9393
StackTraceElement[] innerTrace = innerMostException.getStackTrace();
94-
int currentIdx = 0;
9594
boolean snapshotAssigned = false;
9695
List<Snapshot> snapshots = state.getSnapshots();
9796
for (int i = 0; i < snapshots.size(); i++) {
9897
Snapshot snapshot = snapshots.get(i);
99-
String className = snapshot.getProbe().getLocation().getType();
100-
String methodName = snapshot.getProbe().getLocation().getMethod();
101-
while (currentIdx < innerTrace.length
102-
&& !innerTrace[currentIdx].getClassName().equals(className)
103-
&& !innerTrace[currentIdx].getMethodName().equals(methodName)) {
104-
currentIdx++;
98+
int currentIdx = innerTrace.length - snapshot.getStack().size();
99+
if (!sanityCheckSnapshotAssignment(snapshot, innerTrace, currentIdx)) {
100+
continue;
105101
}
106-
int frameIndex = mapping[currentIdx++];
102+
int frameIndex = mapping[currentIdx];
107103
if (frameIndex == -1) {
108104
continue;
109105
}
@@ -124,6 +120,18 @@ private static void processSnapshotsAndSetTags(
124120
}
125121
}
126122

123+
private static boolean sanityCheckSnapshotAssignment(
124+
Snapshot snapshot, StackTraceElement[] innerTrace, int currentIdx) {
125+
String className = snapshot.getProbe().getLocation().getType();
126+
String methodName = snapshot.getProbe().getLocation().getMethod();
127+
if (!className.equals(innerTrace[currentIdx].getClassName())
128+
|| !methodName.equals(innerTrace[currentIdx].getMethodName())) {
129+
LOGGER.warn("issue when assigning snapshot to frame: {} {}", className, methodName);
130+
return false;
131+
}
132+
return true;
133+
}
134+
127135
ExceptionProbeManager getExceptionProbeManager() {
128136
return exceptionProbeManager;
129137
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ExceptionHelper.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,25 @@ private static void internalFlattenStackTrace(
9797
}
9898
}
9999

100+
// Because flattened stack traces are organized with first frames at the bottom I need to follow
101+
// the order of the first frame and wrap around the array with a modulo to continue the matching
100102
public static int[] createThrowableMapping(Throwable innerMost, Throwable current) {
101-
StackTraceElement[] trace = innerMost.getStackTrace();
103+
StackTraceElement[] innerTrace = innerMost.getStackTrace();
102104
StackTraceElement[] currentTrace = flattenStackTrace(current);
103-
int[] mapping = new int[trace.length];
104-
for (int i = 0; i < trace.length; i++) {
105+
int[] mapping = new int[innerTrace.length];
106+
int currentIdx = 0;
107+
for (int i = 0; i < innerTrace.length; i++) {
105108
mapping[i] = -1;
106-
for (int j = 0; j < currentTrace.length; j++) {
107-
if (trace[i].equals(currentTrace[j])) {
108-
mapping[i] = j;
109+
int count = currentTrace.length;
110+
int idx = currentIdx;
111+
while (count > 0) {
112+
if (innerTrace[i].equals(currentTrace[idx])) {
113+
mapping[i] = idx;
114+
currentIdx = (idx + 1) % currentTrace.length;
109115
break;
110116
}
117+
idx = (idx + 1) % currentTrace.length;
118+
count--;
111119
}
112120
}
113121
return mapping;

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java

Lines changed: 14 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static com.datadog.debugger.util.MoshiSnapshotHelper.NOT_CAPTURED_REASON;
77
import static com.datadog.debugger.util.MoshiSnapshotHelper.REDACTED_IDENT_REASON;
88
import static com.datadog.debugger.util.MoshiSnapshotHelper.REDACTED_TYPE_REASON;
9+
import static com.datadog.debugger.util.MoshiSnapshotTestHelper.VALUE_ADAPTER;
910
import static com.datadog.debugger.util.TestHelper.setFieldInConfig;
1011
import static datadog.trace.bootstrap.debugger.util.Redaction.REDACTED_VALUE;
1112
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -105,8 +106,6 @@ public class CapturedSnapshotTest {
105106
private static final ProbeId PROBE_ID2 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f7", 0);
106107
private static final ProbeId PROBE_ID3 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f8", 0);
107108
private static final String SERVICE_NAME = "service-name";
108-
private static final JsonAdapter<CapturedContext.CapturedValue> VALUE_ADAPTER =
109-
new MoshiSnapshotTestHelper.CapturedValueAdapter();
110109
private static final JsonAdapter<Map<String, Object>> GENERIC_ADAPTER =
111110
MoshiHelper.createGenericAdapter();
112111

@@ -1074,9 +1073,10 @@ public void staticFieldCondition() throws IOException, URISyntaxException {
10741073
Map<String, CapturedContext.CapturedValue> staticFields =
10751074
snapshot.getCaptures().getReturn().getStaticFields();
10761075
assertEquals(4, staticFields.size());
1077-
assertEquals("foo", getValue(staticFields.get("strField")));
1078-
assertEquals("1001", getValue(staticFields.get("intField")));
1079-
assertEquals(String.valueOf(Math.PI), getValue(staticFields.get("doubleField")));
1076+
assertEquals("foo", MoshiSnapshotTestHelper.getValue(staticFields.get("strField")));
1077+
assertEquals("1001", MoshiSnapshotTestHelper.getValue(staticFields.get("intField")));
1078+
assertEquals(
1079+
String.valueOf(Math.PI), MoshiSnapshotTestHelper.getValue(staticFields.get("doubleField")));
10801080
assertTrue(staticFields.containsKey("intArrayField"));
10811081
}
10821082

@@ -1407,11 +1407,11 @@ public void staticInheritedFields() throws IOException, URISyntaxException {
14071407
Map<String, CapturedContext.CapturedValue> staticFields =
14081408
snapshot.getCaptures().getReturn().getStaticFields();
14091409
assertEquals(7, staticFields.size());
1410-
assertEquals("barfoo", getValue(staticFields.get("strValue")));
1411-
assertEquals("48", getValue(staticFields.get("intValue")));
1412-
assertEquals("6.28", getValue(staticFields.get("doubleValue")));
1413-
assertEquals("[1, 2, 3, 4]", getValue(staticFields.get("longValues")));
1414-
assertEquals("[foo, bar]", getValue(staticFields.get("strValues")));
1410+
assertEquals("barfoo", MoshiSnapshotTestHelper.getValue(staticFields.get("strValue")));
1411+
assertEquals("48", MoshiSnapshotTestHelper.getValue(staticFields.get("intValue")));
1412+
assertEquals("6.28", MoshiSnapshotTestHelper.getValue(staticFields.get("doubleValue")));
1413+
assertEquals("[1, 2, 3, 4]", MoshiSnapshotTestHelper.getValue(staticFields.get("longValues")));
1414+
assertEquals("[foo, bar]", MoshiSnapshotTestHelper.getValue(staticFields.get("strValues")));
14151415
}
14161416

14171417
@Test
@@ -2305,14 +2305,14 @@ private void assertCaptureArgs(
23052305
CapturedContext context, String name, String typeName, String value) {
23062306
CapturedContext.CapturedValue capturedValue = context.getArguments().get(name);
23072307
assertEquals(typeName, capturedValue.getType());
2308-
assertEquals(value, getValue(capturedValue));
2308+
assertEquals(value, MoshiSnapshotTestHelper.getValue(capturedValue));
23092309
}
23102310

23112311
private void assertCaptureLocals(
23122312
CapturedContext context, String name, String typeName, String value) {
23132313
CapturedContext.CapturedValue localVar = context.getLocals().get(name);
23142314
assertEquals(typeName, localVar.getType());
2315-
assertEquals(value, getValue(localVar));
2315+
assertEquals(value, MoshiSnapshotTestHelper.getValue(localVar));
23162316
}
23172317

23182318
private void assertCaptureLocals(
@@ -2335,7 +2335,7 @@ private void assertCaptureFields(
23352335
CapturedContext context, String name, String typeName, String value) {
23362336
CapturedContext.CapturedValue field = context.getFields().get(name);
23372337
assertEquals(typeName, field.getType());
2338-
assertEquals(value, getValue(field));
2338+
assertEquals(value, MoshiSnapshotTestHelper.getValue(field));
23392339
}
23402340

23412341
private void assertCaptureFields(
@@ -2385,7 +2385,7 @@ private void assertCaptureFieldCount(CapturedContext context, int expectedFieldC
23852385
private void assertCaptureReturnValue(CapturedContext context, String typeName, String value) {
23862386
CapturedContext.CapturedValue returnValue = context.getLocals().get("@return");
23872387
assertEquals(typeName, returnValue.getType());
2388-
assertEquals(value, getValue(returnValue));
2388+
assertEquals(value, MoshiSnapshotTestHelper.getValue(returnValue));
23892389
}
23902390

23912391
private void assertCaptureReturnValue(
@@ -2425,56 +2425,6 @@ private void assertCaptureThrowable(
24252425
assertEquals(lineNumber, throwable.getStacktrace().get(0).getLineNumber());
24262426
}
24272427

2428-
private static String getValue(CapturedContext.CapturedValue capturedValue) {
2429-
CapturedContext.CapturedValue valued = null;
2430-
try {
2431-
valued = VALUE_ADAPTER.fromJson(capturedValue.getStrValue());
2432-
if (valued.getNotCapturedReason() != null) {
2433-
Assertions.fail("NotCapturedReason: " + valued.getNotCapturedReason());
2434-
}
2435-
Object obj = valued.getValue();
2436-
if (obj != null && obj.getClass().isArray()) {
2437-
if (obj.getClass().getComponentType().isPrimitive()) {
2438-
return primitiveArrayToString(obj);
2439-
}
2440-
return Arrays.toString((Object[]) obj);
2441-
}
2442-
return obj != null ? String.valueOf(obj) : null;
2443-
} catch (IOException e) {
2444-
e.printStackTrace();
2445-
return null;
2446-
}
2447-
}
2448-
2449-
private static String primitiveArrayToString(Object obj) {
2450-
Class<?> componentType = obj.getClass().getComponentType();
2451-
if (componentType == long.class) {
2452-
return Arrays.toString((long[]) obj);
2453-
}
2454-
if (componentType == int.class) {
2455-
return Arrays.toString((int[]) obj);
2456-
}
2457-
if (componentType == short.class) {
2458-
return Arrays.toString((short[]) obj);
2459-
}
2460-
if (componentType == char.class) {
2461-
return Arrays.toString((char[]) obj);
2462-
}
2463-
if (componentType == byte.class) {
2464-
return Arrays.toString((byte[]) obj);
2465-
}
2466-
if (componentType == boolean.class) {
2467-
return Arrays.toString((boolean[]) obj);
2468-
}
2469-
if (componentType == float.class) {
2470-
return Arrays.toString((float[]) obj);
2471-
}
2472-
if (componentType == double.class) {
2473-
return Arrays.toString((double[]) obj);
2474-
}
2475-
return null;
2476-
}
2477-
24782428
public static Map<String, CapturedContext.CapturedValue> getFields(
24792429
CapturedContext.CapturedValue capturedValue) {
24802430
try {

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public void methodActiveSpanSynthException() throws IOException, URISyntaxExcept
238238
public void lineActiveSpanSimpleTag() throws IOException, URISyntaxException {
239239
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20";
240240
SpanDecorationProbe.Decoration decoration = createDecoration("tag1", "{arg}");
241-
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 44);
241+
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 47);
242242
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
243243
int result = Reflect.on(testClass).call("main", "1").get();
244244
assertEquals(84, result);
@@ -271,7 +271,7 @@ public void lineActiveSpanCondition() throws IOException, URISyntaxException {
271271
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20";
272272
SpanDecorationProbe.Decoration decoration =
273273
createDecoration(eq(ref("arg"), value("5")), "arg == '5'", "tag1", "{arg}");
274-
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 44);
274+
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 47);
275275
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
276276
for (int i = 0; i < 10; i++) {
277277
int result = Reflect.on(testClass).call("main", String.valueOf(i)).get();
@@ -287,7 +287,7 @@ public void lineActiveSpanInvalidCondition() throws IOException, URISyntaxExcept
287287
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20";
288288
SpanDecorationProbe.Decoration decoration =
289289
createDecoration(eq(ref("noarg"), value("5")), "arg == '5'", "tag1", "{arg}");
290-
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 44);
290+
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 47);
291291
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
292292
int result = Reflect.on(testClass).call("main", "5").get();
293293
assertEquals(84, result);

0 commit comments

Comments
 (0)