Skip to content

Commit 0ba2505

Browse files
committed
Extract line information as injectible lines
For SymDB we add injectible lines into method scope to provide information about executable line of code where we can put a line probe. We are using the LineNumberTable of each method, sort and make ranges about them.
1 parent 29ed9f5 commit 0ba2505

File tree

5 files changed

+182
-16
lines changed

5 files changed

+182
-16
lines changed

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Scope.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@
44
import java.util.List;
55

66
public class Scope {
7+
8+
public static class LineRange {
9+
final int start;
10+
final int end;
11+
12+
public LineRange(int start, int end) {
13+
this.start = start;
14+
this.end = end;
15+
}
16+
}
17+
718
@Json(name = "scope_type")
819
private final ScopeType scopeType;
920

@@ -16,6 +27,12 @@ public class Scope {
1627
@Json(name = "end_line")
1728
private final int endLine;
1829

30+
@Json(name = "has_injectible_lines")
31+
private final boolean hasInjectibleLines;
32+
33+
@Json(name = "injectible_lines")
34+
private final List<LineRange> injectibleLines;
35+
1936
private final String name;
2037

2138
@Json(name = "language_specifics")
@@ -30,6 +47,8 @@ public Scope(
3047
int startLine,
3148
int endLine,
3249
String name,
50+
boolean hasInjectibleLines,
51+
List<LineRange> injectibleLines,
3352
LanguageSpecifics languageSpecifics,
3453
List<Symbol> symbols,
3554
List<Scope> scopes) {
@@ -38,6 +57,8 @@ public Scope(
3857
this.startLine = startLine;
3958
this.endLine = endLine;
4059
this.name = name;
60+
this.hasInjectibleLines = hasInjectibleLines;
61+
this.injectibleLines = injectibleLines;
4162
this.languageSpecifics = languageSpecifics;
4263
this.symbols = symbols;
4364
this.scopes = scopes;
@@ -63,6 +84,14 @@ public String getName() {
6384
return name;
6485
}
6586

87+
public boolean hasInjectibleLines() {
88+
return hasInjectibleLines;
89+
}
90+
91+
public List<LineRange> getInjectibleLines() {
92+
return injectibleLines;
93+
}
94+
6695
public LanguageSpecifics getLanguageSpecifics() {
6796
return languageSpecifics;
6897
}
@@ -110,6 +139,8 @@ public static class Builder {
110139
private final int startLine;
111140
private final int endLine;
112141
private String name;
142+
private boolean hasInjectibleLines;
143+
private List<LineRange> injectibleLines;
113144
private LanguageSpecifics languageSpecifics;
114145
private List<Symbol> symbols;
115146
private List<Scope> scopes;
@@ -126,6 +157,16 @@ public Builder name(String name) {
126157
return this;
127158
}
128159

160+
public Builder hasInjectibleLines(boolean hasInjectibleLines) {
161+
this.hasInjectibleLines = hasInjectibleLines;
162+
return this;
163+
}
164+
165+
public Builder injectibleLines(List<LineRange> injectibleLines) {
166+
this.injectibleLines = injectibleLines;
167+
return this;
168+
}
169+
129170
public Builder languageSpecifics(LanguageSpecifics languageSpecifics) {
130171
this.languageSpecifics = languageSpecifics;
131172
return this;
@@ -143,7 +184,16 @@ public Builder scopes(List<Scope> scopes) {
143184

144185
public Scope build() {
145186
return new Scope(
146-
scopeType, sourceFile, startLine, endLine, name, languageSpecifics, symbols, scopes);
187+
scopeType,
188+
sourceFile,
189+
startLine,
190+
endLine,
191+
name,
192+
hasInjectibleLines,
193+
injectibleLines,
194+
languageSpecifics,
195+
symbols,
196+
scopes);
147197
}
148198
}
149199
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolExtractor.java

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
import java.util.Collection;
1111
import java.util.Collections;
1212
import java.util.HashMap;
13+
import java.util.HashSet;
1314
import java.util.LinkedHashMap;
1415
import java.util.List;
1516
import java.util.Map;
17+
import java.util.Set;
1618
import java.util.stream.Collectors;
1719
import org.objectweb.asm.ClassReader;
1820
import org.objectweb.asm.Label;
@@ -124,6 +126,8 @@ private static List<Scope> extractMethods(ClassNode classNode, String sourceFile
124126
.name(method.name)
125127
.scopes(varScopes)
126128
.symbols(methodSymbols)
129+
.hasInjectibleLines(!methodLineInfo.ranges.isEmpty())
130+
.injectibleLines(methodLineInfo.ranges)
127131
.languageSpecifics(methodSpecifics)
128132
.build();
129133
methodScopes.add(methodScope);
@@ -431,26 +435,47 @@ private static Scope maxScope(Scope scope1, Scope scope2) {
431435
: scope1;
432436
}
433437

434-
private static int getFirstLine(MethodNode methodNode) {
435-
AbstractInsnNode node = methodNode.instructions.getFirst();
436-
while (node != null) {
437-
if (node.getType() == AbstractInsnNode.LINE) {
438-
LineNumberNode lineNumberNode = (LineNumberNode) node;
439-
return lineNumberNode.line;
438+
static List<Scope.LineRange> buildRanges(List<Integer> sortedLineNo) {
439+
if (sortedLineNo.isEmpty()) {
440+
return Collections.emptyList();
441+
}
442+
List<Scope.LineRange> ranges = new ArrayList<>();
443+
int start = sortedLineNo.get(0);
444+
int previous = start;
445+
int i = 1;
446+
outer:
447+
while (i < sortedLineNo.size()) {
448+
int currentLineNo = sortedLineNo.get(i);
449+
while (currentLineNo == previous + 1) {
450+
i++;
451+
previous++;
452+
if (i < sortedLineNo.size()) {
453+
currentLineNo = sortedLineNo.get(i);
454+
} else {
455+
break outer;
456+
}
440457
}
441-
node = node.getNext();
458+
ranges.add(new Scope.LineRange(start, previous));
459+
start = currentLineNo;
460+
previous = start;
461+
i++;
442462
}
443-
return 0;
463+
ranges.add(new Scope.LineRange(start, previous));
464+
return ranges;
444465
}
445466

446467
private static MethodLineInfo extractMethodLineInfo(MethodNode methodNode) {
447468
Map<Label, Integer> map = new HashMap<>();
448-
int startLine = getFirstLine(methodNode);
449-
int maxLine = startLine;
469+
List<Integer> lineNo = new ArrayList<>();
470+
Set<Integer> dedupSet = new HashSet<>();
450471
AbstractInsnNode node = methodNode.instructions.getFirst();
472+
int maxLine = 0;
451473
while (node != null) {
452474
if (node.getType() == AbstractInsnNode.LINE) {
453475
LineNumberNode lineNumberNode = (LineNumberNode) node;
476+
if (dedupSet.add(lineNumberNode.line)) {
477+
lineNo.add(lineNumberNode.line);
478+
}
454479
maxLine = Math.max(lineNumberNode.line, maxLine);
455480
}
456481
if (node.getType() == AbstractInsnNode.LABEL) {
@@ -461,7 +486,10 @@ private static MethodLineInfo extractMethodLineInfo(MethodNode methodNode) {
461486
}
462487
node = node.getNext();
463488
}
464-
return new MethodLineInfo(startLine, maxLine, map);
489+
lineNo.sort(Integer::compareTo);
490+
int startLine = lineNo.isEmpty() ? 0 : lineNo.get(0);
491+
List<Scope.LineRange> ranges = buildRanges(lineNo);
492+
return new MethodLineInfo(startLine, maxLine, map, ranges);
465493
}
466494

467495
private static ClassNode parseClassFile(byte[] classfileBuffer) {
@@ -475,11 +503,15 @@ public static class MethodLineInfo {
475503
final int start;
476504
final int end;
477505
final Map<Label, Integer> lineMap;
506+
final List<Scope.LineRange> ranges;
478507

479-
public MethodLineInfo(int start, int end, Map<Label, Integer> lineMap) {
508+
public MethodLineInfo(
509+
int start, int end, Map<Label, Integer> lineMap, List<Scope.LineRange> ranges) {
480510
this.start = start;
481511
this.end = end;
482512
this.lineMap = lineMap;
513+
514+
this.ranges = ranges;
483515
}
484516
}
485517
}

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/sink/SymbolSinkTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void testSimpleFlush() {
4040
assertEquals("file", symbolContent.getPartName());
4141
assertEquals("file.json", symbolContent.getFileName());
4242
assertEquals(
43-
"{\"language\":\"JAVA\",\"scopes\":[{\"end_line\":0,\"scope_type\":\"JAR\",\"start_line\":0}],\"service\":\"service1\"}",
43+
"{\"language\":\"JAVA\",\"scopes\":[{\"end_line\":0,\"has_injectible_lines\":false,\"scope_type\":\"JAR\",\"start_line\":0}],\"service\":\"service1\"}",
4444
new String(symbolContent.getContent()));
4545
}
4646

@@ -109,12 +109,12 @@ public void splitByJarScopes() {
109109
}
110110

111111
@Test
112-
public void splitTootManyJarScopes() {
112+
public void splitTooManyJarScopes() {
113113
SymbolUploaderMock symbolUploaderMock = new SymbolUploaderMock();
114114
Config config = mock(Config.class);
115115
when(config.getServiceName()).thenReturn("service1");
116116
when(config.isSymbolDatabaseCompressed()).thenReturn(false);
117-
SymbolSink symbolSink = new SymbolSink(config, symbolUploaderMock, 2048);
117+
SymbolSink symbolSink = new SymbolSink(config, symbolUploaderMock, 4096);
118118
final int NUM_JAR_SCOPES = 21;
119119
for (int i = 0; i < NUM_JAR_SCOPES; i++) {
120120
symbolSink.addScope(

0 commit comments

Comments
 (0)