Skip to content

Commit

Permalink
Merge pull request #6868 from DataDog/jpbempel/stack-depth-assignment
Browse files Browse the repository at this point in the history
Use stack depth to do snapshot assignment
  • Loading branch information
jpbempel committed Apr 4, 2024
2 parents 386364a + 34c20c0 commit 734e3c5
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,15 @@ private static void processSnapshotsAndSetTags(
}
int[] mapping = createThrowableMapping(innerMostException, t);
StackTraceElement[] innerTrace = innerMostException.getStackTrace();
int currentIdx = 0;
boolean snapshotAssigned = false;
List<Snapshot> snapshots = state.getSnapshots();
for (int i = 0; i < snapshots.size(); i++) {
Snapshot snapshot = snapshots.get(i);
String className = snapshot.getProbe().getLocation().getType();
String methodName = snapshot.getProbe().getLocation().getMethod();
while (currentIdx < innerTrace.length
&& !innerTrace[currentIdx].getClassName().equals(className)
&& !innerTrace[currentIdx].getMethodName().equals(methodName)) {
currentIdx++;
int currentIdx = innerTrace.length - snapshot.getStack().size();
if (!sanityCheckSnapshotAssignment(snapshot, innerTrace, currentIdx)) {
continue;
}
int frameIndex = mapping[currentIdx++];
int frameIndex = mapping[currentIdx];
if (frameIndex == -1) {
continue;
}
Expand All @@ -124,6 +120,18 @@ private static void processSnapshotsAndSetTags(
}
}

private static boolean sanityCheckSnapshotAssignment(
Snapshot snapshot, StackTraceElement[] innerTrace, int currentIdx) {
String className = snapshot.getProbe().getLocation().getType();
String methodName = snapshot.getProbe().getLocation().getMethod();
if (!className.equals(innerTrace[currentIdx].getClassName())
|| !methodName.equals(innerTrace[currentIdx].getMethodName())) {
LOGGER.warn("issue when assigning snapshot to frame: {} {}", className, methodName);
return false;
}
return true;
}

ExceptionProbeManager getExceptionProbeManager() {
return exceptionProbeManager;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,25 @@ private static void internalFlattenStackTrace(
}
}

// Because flattened stack traces are organized with first frames at the bottom I need to follow
// the order of the first frame and wrap around the array with a modulo to continue the matching
public static int[] createThrowableMapping(Throwable innerMost, Throwable current) {
StackTraceElement[] trace = innerMost.getStackTrace();
StackTraceElement[] innerTrace = innerMost.getStackTrace();
StackTraceElement[] currentTrace = flattenStackTrace(current);
int[] mapping = new int[trace.length];
for (int i = 0; i < trace.length; i++) {
int[] mapping = new int[innerTrace.length];
int currentIdx = 0;
for (int i = 0; i < innerTrace.length; i++) {
mapping[i] = -1;
for (int j = 0; j < currentTrace.length; j++) {
if (trace[i].equals(currentTrace[j])) {
mapping[i] = j;
int count = currentTrace.length;
int idx = currentIdx;
while (count > 0) {
if (innerTrace[i].equals(currentTrace[idx])) {
mapping[i] = idx;
currentIdx = (idx + 1) % currentTrace.length;
break;
}
idx = (idx + 1) % currentTrace.length;
count--;
}
}
return mapping;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static com.datadog.debugger.util.MoshiSnapshotHelper.NOT_CAPTURED_REASON;
import static com.datadog.debugger.util.MoshiSnapshotHelper.REDACTED_IDENT_REASON;
import static com.datadog.debugger.util.MoshiSnapshotHelper.REDACTED_TYPE_REASON;
import static com.datadog.debugger.util.MoshiSnapshotTestHelper.VALUE_ADAPTER;
import static com.datadog.debugger.util.TestHelper.setFieldInConfig;
import static datadog.trace.bootstrap.debugger.util.Redaction.REDACTED_VALUE;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
Expand Down Expand Up @@ -105,8 +106,6 @@ public class CapturedSnapshotTest {
private static final ProbeId PROBE_ID2 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f7", 0);
private static final ProbeId PROBE_ID3 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f8", 0);
private static final String SERVICE_NAME = "service-name";
private static final JsonAdapter<CapturedContext.CapturedValue> VALUE_ADAPTER =
new MoshiSnapshotTestHelper.CapturedValueAdapter();
private static final JsonAdapter<Map<String, Object>> GENERIC_ADAPTER =
MoshiHelper.createGenericAdapter();

Expand Down Expand Up @@ -1074,9 +1073,10 @@ public void staticFieldCondition() throws IOException, URISyntaxException {
Map<String, CapturedContext.CapturedValue> staticFields =
snapshot.getCaptures().getReturn().getStaticFields();
assertEquals(4, staticFields.size());
assertEquals("foo", getValue(staticFields.get("strField")));
assertEquals("1001", getValue(staticFields.get("intField")));
assertEquals(String.valueOf(Math.PI), getValue(staticFields.get("doubleField")));
assertEquals("foo", MoshiSnapshotTestHelper.getValue(staticFields.get("strField")));
assertEquals("1001", MoshiSnapshotTestHelper.getValue(staticFields.get("intField")));
assertEquals(
String.valueOf(Math.PI), MoshiSnapshotTestHelper.getValue(staticFields.get("doubleField")));
assertTrue(staticFields.containsKey("intArrayField"));
}

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

@Test
Expand Down Expand Up @@ -2305,14 +2305,14 @@ private void assertCaptureArgs(
CapturedContext context, String name, String typeName, String value) {
CapturedContext.CapturedValue capturedValue = context.getArguments().get(name);
assertEquals(typeName, capturedValue.getType());
assertEquals(value, getValue(capturedValue));
assertEquals(value, MoshiSnapshotTestHelper.getValue(capturedValue));
}

private void assertCaptureLocals(
CapturedContext context, String name, String typeName, String value) {
CapturedContext.CapturedValue localVar = context.getLocals().get(name);
assertEquals(typeName, localVar.getType());
assertEquals(value, getValue(localVar));
assertEquals(value, MoshiSnapshotTestHelper.getValue(localVar));
}

private void assertCaptureLocals(
Expand All @@ -2335,7 +2335,7 @@ private void assertCaptureFields(
CapturedContext context, String name, String typeName, String value) {
CapturedContext.CapturedValue field = context.getFields().get(name);
assertEquals(typeName, field.getType());
assertEquals(value, getValue(field));
assertEquals(value, MoshiSnapshotTestHelper.getValue(field));
}

private void assertCaptureFields(
Expand Down Expand Up @@ -2385,7 +2385,7 @@ private void assertCaptureFieldCount(CapturedContext context, int expectedFieldC
private void assertCaptureReturnValue(CapturedContext context, String typeName, String value) {
CapturedContext.CapturedValue returnValue = context.getLocals().get("@return");
assertEquals(typeName, returnValue.getType());
assertEquals(value, getValue(returnValue));
assertEquals(value, MoshiSnapshotTestHelper.getValue(returnValue));
}

private void assertCaptureReturnValue(
Expand Down Expand Up @@ -2425,56 +2425,6 @@ private void assertCaptureThrowable(
assertEquals(lineNumber, throwable.getStacktrace().get(0).getLineNumber());
}

private static String getValue(CapturedContext.CapturedValue capturedValue) {
CapturedContext.CapturedValue valued = null;
try {
valued = VALUE_ADAPTER.fromJson(capturedValue.getStrValue());
if (valued.getNotCapturedReason() != null) {
Assertions.fail("NotCapturedReason: " + valued.getNotCapturedReason());
}
Object obj = valued.getValue();
if (obj != null && obj.getClass().isArray()) {
if (obj.getClass().getComponentType().isPrimitive()) {
return primitiveArrayToString(obj);
}
return Arrays.toString((Object[]) obj);
}
return obj != null ? String.valueOf(obj) : null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

private static String primitiveArrayToString(Object obj) {
Class<?> componentType = obj.getClass().getComponentType();
if (componentType == long.class) {
return Arrays.toString((long[]) obj);
}
if (componentType == int.class) {
return Arrays.toString((int[]) obj);
}
if (componentType == short.class) {
return Arrays.toString((short[]) obj);
}
if (componentType == char.class) {
return Arrays.toString((char[]) obj);
}
if (componentType == byte.class) {
return Arrays.toString((byte[]) obj);
}
if (componentType == boolean.class) {
return Arrays.toString((boolean[]) obj);
}
if (componentType == float.class) {
return Arrays.toString((float[]) obj);
}
if (componentType == double.class) {
return Arrays.toString((double[]) obj);
}
return null;
}

public static Map<String, CapturedContext.CapturedValue> getFields(
CapturedContext.CapturedValue capturedValue) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public void methodActiveSpanSynthException() throws IOException, URISyntaxExcept
public void lineActiveSpanSimpleTag() throws IOException, URISyntaxException {
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20";
SpanDecorationProbe.Decoration decoration = createDecoration("tag1", "{arg}");
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 44);
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 47);
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
int result = Reflect.on(testClass).call("main", "1").get();
assertEquals(84, result);
Expand Down Expand Up @@ -271,7 +271,7 @@ public void lineActiveSpanCondition() throws IOException, URISyntaxException {
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20";
SpanDecorationProbe.Decoration decoration =
createDecoration(eq(ref("arg"), value("5")), "arg == '5'", "tag1", "{arg}");
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 44);
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 47);
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
for (int i = 0; i < 10; i++) {
int result = Reflect.on(testClass).call("main", String.valueOf(i)).get();
Expand All @@ -287,7 +287,7 @@ public void lineActiveSpanInvalidCondition() throws IOException, URISyntaxExcept
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20";
SpanDecorationProbe.Decoration decoration =
createDecoration(eq(ref("noarg"), value("5")), "arg == '5'", "tag1", "{arg}");
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 44);
installSingleSpanDecoration(CLASS_NAME, ACTIVE, decoration, "CapturedSnapshot20.java", 47);
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
int result = Reflect.on(testClass).call("main", "5").get();
assertEquals(84, result);
Expand Down
Loading

0 comments on commit 734e3c5

Please sign in to comment.