Skip to content

Commit 14a9d87

Browse files
authored
Merge branch 'master' into joey/aws-sns
2 parents 640e792 + dadbd53 commit 14a9d87

File tree

7 files changed

+100
-28
lines changed

7 files changed

+100
-28
lines changed

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -655,10 +655,15 @@ private MethodNode matchSourceFile(
655655
}
656656
Where.SourceLine sourceLine = lines[0]; // assume only 1 range
657657
int matchingLine = sourceLine.getFrom();
658-
MethodNode matchingMethod = classFileLines.getMethodByLine(matchingLine);
659-
if (matchingMethod != null) {
660-
log.debug("Found lineNode {} method: {}", matchingLine, matchingMethod.name);
661-
return matchingMethod;
658+
List<MethodNode> matchingMethods = classFileLines.getMethodsByLine(matchingLine);
659+
if (matchingMethods != null) {
660+
matchingMethods.forEach(
661+
methodNode -> {
662+
log.debug("Found lineNode {} method: {}", matchingLine, methodNode.name);
663+
});
664+
// pick the first matching method.
665+
// TODO need a way to disambiguate if multiple methods match the same line
666+
return matchingMethods.isEmpty() ? null : matchingMethods.get(0);
662667
}
663668
log.debug("Cannot find line: {} in class {}", matchingLine, classNode.name);
664669
return null;

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/Where.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,13 @@ protected static SourceLine[] sourceLines(String[] defs) {
6464

6565
public static Where convertLineToMethod(Where lineWhere, ClassFileLines classFileLines) {
6666
if (lineWhere.methodName != null && lineWhere.lines != null) {
67-
MethodNode method = classFileLines.getMethodByLine(lineWhere.lines[0].getFrom());
68-
return new Where(lineWhere.typeName, method.name, method.desc, (SourceLine[]) null, null);
67+
List<MethodNode> methodsByLine =
68+
classFileLines.getMethodsByLine(lineWhere.lines[0].getFrom());
69+
if (methodsByLine != null && !methodsByLine.isEmpty()) {
70+
// pick the first method, as we can have multiple methods (lambdas) on the same line
71+
MethodNode method = methodsByLine.get(0);
72+
return new Where(lineWhere.typeName, method.name, method.desc, (SourceLine[]) null, null);
73+
}
6974
}
7075
throw new IllegalArgumentException("Invalid where to convert from line to method " + lineWhere);
7176
}
@@ -122,7 +127,11 @@ public boolean isMethodMatching(MethodNode methodNode, ClassFileLines classFileL
122127
return true;
123128
}
124129
// try matching by line
125-
return classFileLines.getMethodByLine(lines[0].getFrom()) == methodNode;
130+
List<MethodNode> methodsByLine = classFileLines.getMethodsByLine(lines[0].getFrom());
131+
if (methodsByLine == null || methodsByLine.isEmpty()) {
132+
return false;
133+
}
134+
return methodsByLine.stream().anyMatch(m -> m == methodNode);
126135
}
127136
if (signature.equals("*") || signature.equals(targetMethodDescriptor)) {
128137
return true;

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.datadog.debugger.util;
22

3+
import java.util.ArrayList;
34
import java.util.HashMap;
5+
import java.util.List;
46
import java.util.Map;
57
import java.util.TreeMap;
68
import org.objectweb.asm.tree.AbstractInsnNode;
@@ -10,7 +12,7 @@
1012
import org.objectweb.asm.tree.MethodNode;
1113

1214
public class ClassFileLines {
13-
private final Map<Integer, MethodNode> methodByLine = new HashMap<>();
15+
private final Map<Integer, List<MethodNode>> methodByLine = new HashMap<>();
1416
private final TreeMap<Integer, LabelNode> lineLabels = new TreeMap<>();
1517

1618
public ClassFileLines(ClassNode classNode) {
@@ -19,7 +21,14 @@ public ClassFileLines(ClassNode classNode) {
1921
while (currentNode != null) {
2022
if (currentNode.getType() == AbstractInsnNode.LINE) {
2123
LineNumberNode lineNode = (LineNumberNode) currentNode;
22-
methodByLine.put(lineNode.line, methodNode);
24+
// on the same line, we can have multiple methods (lambdas, inner classes, etc)
25+
List<MethodNode> methodNodes =
26+
methodByLine.computeIfAbsent(lineNode.line, k -> new ArrayList<>());
27+
// We are not using a Set here to keep the order of the methods
28+
// We assume also that the number of methods per line is small
29+
if (!methodNodes.contains(methodNode)) {
30+
methodNodes.add(methodNode);
31+
}
2332
// we are putting the line only the first time in the map.
2433
// for-loops can generate 2 line nodes for the same line, 1 before the loop, 1 for the
2534
// incrementation part.
@@ -31,7 +40,7 @@ public ClassFileLines(ClassNode classNode) {
3140
}
3241
}
3342

34-
public MethodNode getMethodByLine(int line) {
43+
public List<MethodNode> getMethodsByLine(int line) {
3544
return methodByLine.get(line);
3645
}
3746

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,12 +1443,12 @@ public void staticLambda() throws IOException, URISyntaxException {
14431443
setCorrelationSingleton(spyCorrelationAccess);
14441444
doReturn(true).when(spyCorrelationAccess).isAvailable();
14451445
TestSnapshotListener listener =
1446-
installProbes(CLASS_NAME, createProbe(PROBE_ID, CLASS_NAME, null, null, "33"));
1446+
installProbes(CLASS_NAME, createProbe(PROBE_ID, CLASS_NAME, null, null, "37"));
14471447
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
14481448
int result = Reflect.onClass(testClass).call("main", "static", "email@address").get();
14491449
assertEquals(8, result);
14501450
Snapshot snapshot = assertOneSnapshot(listener);
1451-
CapturedContext context = snapshot.getCaptures().getLines().get(33);
1451+
CapturedContext context = snapshot.getCaptures().getLines().get(37);
14521452
assertNotNull(context);
14531453
assertCaptureLocals(context, "idx", "int", "5");
14541454
}
@@ -1460,17 +1460,34 @@ public void capturingLambda() throws IOException, URISyntaxException {
14601460
setCorrelationSingleton(spyCorrelationAccess);
14611461
doReturn(true).when(spyCorrelationAccess).isAvailable();
14621462
TestSnapshotListener listener =
1463-
installProbes(CLASS_NAME, createProbe(PROBE_ID, CLASS_NAME, null, null, "44"));
1463+
installProbes(CLASS_NAME, createProbe(PROBE_ID, CLASS_NAME, null, null, "48"));
14641464
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
14651465
int result = Reflect.onClass(testClass).call("main", "capturing", "email@address").get();
14661466
assertEquals(8, result);
14671467
Snapshot snapshot = assertOneSnapshot(listener);
1468-
CapturedContext context = snapshot.getCaptures().getLines().get(44);
1468+
CapturedContext context = snapshot.getCaptures().getLines().get(48);
14691469
assertNotNull(context);
14701470
assertCaptureLocals(context, "idx", "int", "5");
14711471
assertCaptureFields(context, "strValue", "java.lang.String", "email@address");
14721472
}
14731473

1474+
@Test
1475+
public void multiLambdas() throws IOException, URISyntaxException {
1476+
final String CLASS_NAME = "CapturedSnapshot07";
1477+
CorrelationAccess spyCorrelationAccess = spy(CorrelationAccess.instance());
1478+
setCorrelationSingleton(spyCorrelationAccess);
1479+
doReturn(true).when(spyCorrelationAccess).isAvailable();
1480+
TestSnapshotListener listener =
1481+
installProbes(CLASS_NAME, createProbe(PROBE_ID, CLASS_NAME, null, null, "58"));
1482+
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
1483+
int result = Reflect.onClass(testClass).call("main", "multi", "FOO1,FOO2,FOO3").get();
1484+
assertEquals(3, result);
1485+
Snapshot snapshot = assertOneSnapshot(listener);
1486+
CapturedContext context = snapshot.getCaptures().getLines().get(58);
1487+
assertNotNull(context);
1488+
assertCaptureArgs(context, "arg", String.class.getTypeName(), "FOO1,FOO2,FOO3");
1489+
}
1490+
14741491
@Test
14751492
public void tracerInstrumentedClass() throws Exception {
14761493
DebuggerContext.initClassFilter(new DenyListHelper(null));

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/WhereTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.datadog.debugger.util.MoshiHelper;
99
import com.squareup.moshi.JsonAdapter;
1010
import java.io.IOException;
11+
import java.util.Arrays;
1112
import org.junit.jupiter.api.Assertions;
1213
import org.junit.jupiter.api.Test;
1314
import org.objectweb.asm.tree.MethodNode;
@@ -119,7 +120,7 @@ public void methodMatching() {
119120
where = new Where("MyClass", "myMethod", null, new String[] {"42"}, null);
120121
ClassFileLines classFileLines = mock(ClassFileLines.class);
121122
MethodNode myMethodNode = createMethodNode("myMethod", "()V");
122-
when(classFileLines.getMethodByLine(42)).thenReturn(myMethodNode);
123+
when(classFileLines.getMethodsByLine(42)).thenReturn(Arrays.asList(myMethodNode));
123124
assertTrue(where.isMethodMatching(myMethodNode, classFileLines));
124125
}
125126

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/ClassFileLinesTest.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.junit.jupiter.api.Assertions.assertTrue;
66
import static utils.InstrumentationTestHelper.compile;
77

8+
import java.util.List;
89
import org.junit.jupiter.api.Test;
910
import org.objectweb.asm.ClassReader;
1011
import org.objectweb.asm.tree.ClassNode;
@@ -21,7 +22,7 @@ void isEmptyReturnTrue() {
2122

2223
@Test
2324
void ValidateLineMapReturnLinesInRangeOrNull() throws Exception {
24-
ClassFileLines classFileLines = createClassFileLines();
25+
ClassFileLines classFileLines = createClassFileLines("CapturedSnapshot02");
2526
LabelNode line13 = classFileLines.getLineLabel(13);
2627
LabelNode line17 = classFileLines.getLineLabel(17);
2728

@@ -31,34 +32,54 @@ void ValidateLineMapReturnLinesInRangeOrNull() throws Exception {
3132
}
3233

3334
@Test
34-
void getMethodByLine() throws Exception {
35-
ClassFileLines classFileLines = createClassFileLines();
36-
MethodNode method13 = classFileLines.getMethodByLine(13);
35+
void getMethodsByLine() throws Exception {
36+
ClassFileLines classFileLines = createClassFileLines("CapturedSnapshot02");
37+
MethodNode method13 = classFileLines.getMethodsByLine(13).get(0);
3738
assertEquals("<init>", method13.name);
3839
assertEquals("()V", method13.desc);
39-
MethodNode method17 = classFileLines.getMethodByLine(17);
40+
MethodNode method17 = classFileLines.getMethodsByLine(17).get(0);
4041
assertEquals("<init>", method17.name);
4142
assertEquals("(Ljava/lang/Throwable;)V", method17.desc);
42-
MethodNode method26 = classFileLines.getMethodByLine(26);
43+
MethodNode method26 = classFileLines.getMethodsByLine(26).get(0);
4344
assertEquals("<init>", method26.name);
4445
assertEquals("(Ljava/lang/String;Ljava/lang/Object;)V", method26.desc);
45-
MethodNode method32 = classFileLines.getMethodByLine(32);
46+
MethodNode method32 = classFileLines.getMethodsByLine(32).get(0);
4647
assertEquals("createObject", method32.name);
4748
assertEquals("()Ljava/lang/Object;", method32.desc);
48-
MethodNode method37 = classFileLines.getMethodByLine(37);
49+
MethodNode method37 = classFileLines.getMethodsByLine(37).get(0);
4950
assertEquals("f", method37.name);
5051
assertEquals("()V", method37.desc);
51-
MethodNode method44 = classFileLines.getMethodByLine(44);
52+
MethodNode method44 = classFileLines.getMethodsByLine(44).get(0);
5253
assertEquals("synchronizedBlock", method44.name);
5354
assertEquals("(I)I", method44.desc);
54-
MethodNode method54 = classFileLines.getMethodByLine(54);
55+
MethodNode method54 = classFileLines.getMethodsByLine(54).get(0);
5556
assertEquals("main", method54.name);
5657
assertEquals("(Ljava/lang/String;)I", method54.desc);
5758
}
5859

59-
private ClassFileLines createClassFileLines() throws Exception {
60-
final String CLASS_NAME = "CapturedSnapshot02";
61-
byte[] byteBuffer = compile(CLASS_NAME).get(CLASS_NAME);
60+
@Test
61+
void getMethodsByLineWithLambdas() throws Exception {
62+
ClassFileLines classFileLines = createClassFileLines("CapturedSnapshot07");
63+
List<MethodNode> methods57 = classFileLines.getMethodsByLine(57);
64+
// despite having the return on multi line which yield multiple BCI for same line
65+
// this is still the same method
66+
assertEquals(1, methods57.size());
67+
assertEquals("multiLambda", methods57.get(0).name);
68+
assertEquals("(Ljava/lang/String;)I", methods57.get(0).desc);
69+
List<MethodNode> methods58 = classFileLines.getMethodsByLine(58);
70+
assertEquals(3, methods58.size());
71+
assertEquals("multiLambda", methods58.get(0).name);
72+
assertEquals("(Ljava/lang/String;)I", methods58.get(0).desc);
73+
// filter
74+
assertEquals("lambda$multiLambda$3", methods58.get(1).name);
75+
assertEquals("(Ljava/lang/String;)Z", methods58.get(1).desc);
76+
// map
77+
assertEquals("lambda$multiLambda$2", methods58.get(2).name);
78+
assertEquals("(Ljava/lang/String;)Ljava/lang/String;", methods58.get(2).desc);
79+
}
80+
81+
private ClassFileLines createClassFileLines(String className) throws Exception {
82+
byte[] byteBuffer = compile(className).get(className);
6283
ClassReader reader = new ClassReader(byteBuffer);
6384
ClassNode classNode = new ClassNode();
6485
reader.accept(classNode, ClassReader.SKIP_FRAMES);

dd-java-agent/agent-debugger/src/test/resources/CapturedSnapshot07.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public static int main(String mode, String arg) {
2424
cs7.strValue = arg;
2525
return cs7.capturingLambda(arg);
2626
}
27+
if (mode.equals("multi")) {
28+
CapturedSnapshot07 cs7 = new CapturedSnapshot07();
29+
return cs7.multiLambda(arg);
30+
}
2731
return 0;
2832
}
2933

@@ -48,4 +52,10 @@ private int capturingLambda(String arg) {
4852
};
4953
return func.apply(arg).length();
5054
}
55+
56+
private int multiLambda(String arg) {
57+
return (int)Arrays.stream(arg.split(","))
58+
.map(s -> s.toUpperCase()).filter(s -> s.startsWith("FOO"))
59+
.count();
60+
}
5161
}

0 commit comments

Comments
 (0)