Skip to content

Commit bd48676

Browse files
authored
Merge branch 'master' into dougqh/collection-benchmarks
2 parents 42b4b7e + 92d7ae6 commit bd48676

14 files changed

Lines changed: 290 additions & 100 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package datadog.trace.bootstrap;
2+
3+
import org.openjdk.jmh.annotations.Benchmark;
4+
import org.openjdk.jmh.annotations.Fork;
5+
import org.openjdk.jmh.annotations.Measurement;
6+
import org.openjdk.jmh.annotations.Threads;
7+
import org.openjdk.jmh.annotations.Warmup;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
/**
12+
* Benchmark showing impact of ExceptionLogger
13+
*
14+
* <p>NOTE: This benchmark exists to check the efficiency of retrieving the ExceptionLogger.
15+
* Previously, this caused significant allocation.
16+
*/
17+
@Fork(2)
18+
@Warmup(iterations = 2)
19+
@Measurement(iterations = 5)
20+
@Threads(8)
21+
public class ExceptionLoggerBenchmark {
22+
@Benchmark
23+
public Logger getExceptionLogger() {
24+
// This matches what happens in the bytecode weaving that defends against
25+
// exception leaking out of instrumentation.
26+
return LoggerFactory.getLogger(ExceptionLogger.class);
27+
}
28+
}

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/ExceptionLogger.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
*
99
* <p>See datadog.trace.agent.tooling.ExceptionHandlers
1010
*/
11-
public class ExceptionLogger {
11+
public final class ExceptionLogger {
1212
public static final Logger LOGGER = LoggerFactory.getLogger(ExceptionLogger.class);
13+
14+
private ExceptionLogger() {}
1315
}

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

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -206,33 +206,40 @@ private List<Class<?>> detectMethodParameters(
206206
}
207207
List<Class<?>> result = new ArrayList<>();
208208
for (Class<?> changedClass : changedClasses) {
209-
Method[] declaredMethods = changedClass.getDeclaredMethods();
210209
boolean addClass = true;
211-
// capping scanning of methods to 100 to avoid generated class with thousand of methods
212-
// assuming that in those first 100 methods there is at least one with at least one parameter
213-
for (int methodIdx = 0; methodIdx < declaredMethods.length && methodIdx < 100; methodIdx++) {
214-
Method method = declaredMethods[methodIdx];
215-
Parameter[] parameters = method.getParameters();
216-
if (parameters.length == 0) {
217-
continue;
218-
}
219-
if (parameters[0].isNamePresent()) {
220-
if (!SpringHelper.isSpringUsingOnlyMethodParameters(instrumentation)) {
221-
return changedClasses;
210+
try {
211+
Method[] declaredMethods = changedClass.getDeclaredMethods();
212+
// capping scanning of methods to 100 to avoid generated class with thousand of methods
213+
// assuming that in those first 100 methods there is at least one with at least one
214+
// parameter
215+
for (int methodIdx = 0;
216+
methodIdx < declaredMethods.length && methodIdx < 100;
217+
methodIdx++) {
218+
Method method = declaredMethods[methodIdx];
219+
Parameter[] parameters = method.getParameters();
220+
if (parameters.length == 0) {
221+
continue;
222+
}
223+
if (parameters[0].isNamePresent()) {
224+
if (!SpringHelper.isSpringUsingOnlyMethodParameters(instrumentation)) {
225+
return changedClasses;
226+
}
227+
LOGGER.debug(
228+
"Detecting method parameter: method={} param={}, Skipping retransforming this class",
229+
method.getName(),
230+
parameters[0].getName());
231+
// skip the class: compiled with -parameters
232+
reportError(
233+
changes,
234+
"Method Parameters detected, instrumentation not supported for "
235+
+ changedClass.getTypeName());
236+
addClass = false;
222237
}
223-
LOGGER.debug(
224-
"Detecting method parameter: method={} param={}, Skipping retransforming this class",
225-
method.getName(),
226-
parameters[0].getName());
227-
// skip the class: compiled with -parameters
228-
reportError(
229-
changes,
230-
"Method Parameters detected, instrumentation not supported for "
231-
+ changedClass.getTypeName());
232-
addClass = false;
238+
// we found at leat a method with one parameter if name is not present we can stop there
239+
break;
233240
}
234-
// we found at leat a method with one parameter if name is not present we can stop there
235-
break;
241+
} catch (Exception e) {
242+
LOGGER.debug("Exception scanning method parameters", e);
236243
}
237244
if (addClass) {
238245
result.add(changedClass);

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/CapturedContextInstrumenter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,15 @@ private boolean addLineCaptures(ClassFileLines classFileLines) {
119119
int till = sourceLine.getTill();
120120

121121
boolean isSingleLine = from == till;
122-
122+
if (isSingleLine) {
123+
List<MethodNode> methods = classFileLines.getMethodsByLine(from);
124+
if (methods.size() == 1 && "<init>".equals(methods.get(0).name)) {
125+
if (from == classFileLines.getMethodStart(methodNode)) {
126+
reportError("Cannot instrument the first line of a constructor");
127+
return false;
128+
}
129+
}
130+
}
123131
LabelNode beforeLabel = classFileLines.getLineLabel(from);
124132
// single line N capture translates to line range (N, N+1)
125133
LabelNode afterLabel = classFileLines.getLineLabel(till + (isSingleLine ? 1 : 0));

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ private static Scope extractScopes(ClassNode classNode, String jarName) {
5757
int classStartLine = Integer.MAX_VALUE;
5858
int classEndLine = 0;
5959
for (Scope scope : methodScopes) {
60-
classStartLine = Math.min(classStartLine, scope.getStartLine());
61-
classEndLine = Math.max(classEndLine, scope.getEndLine());
60+
if (scope.getStartLine() > 0) {
61+
classStartLine = Math.min(classStartLine, scope.getStartLine());
62+
}
63+
if (scope.getEndLine() > 0) {
64+
classEndLine = Math.max(classEndLine, scope.getEndLine());
65+
}
6266
}
6367
List<Symbol> fields = extractFields(classNode);
6468
LanguageSpecifics classSpecifics =
@@ -505,7 +509,18 @@ private static MethodLineInfo extractMethodLineInfo(
505509
node = node.getNext();
506510
}
507511
lineNo.sort(Integer::compareTo);
508-
int startLine = lineNo.isEmpty() ? 0 : lineNo.get(0);
512+
if (methodNode.name.equals("<init>") && !lineNo.isEmpty()) {
513+
// for constructors we are dropping the first line because it is not instrumentable:
514+
// it needs to call the super or this before
515+
lineNo.remove(0);
516+
}
517+
int startLine;
518+
if (lineNo.isEmpty()) {
519+
startLine = 0;
520+
maxLine = 0;
521+
} else {
522+
startLine = lineNo.get(0);
523+
}
509524
List<Scope.LineRange> ranges = buildRanges(lineNo);
510525
return new MethodLineInfo(startLine, maxLine, map, ranges);
511526
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,22 @@ public void constructor() throws IOException, URISyntaxException {
256256
assertOneSnapshot(listener);
257257
}
258258

259+
@Test
260+
public void constructorFirstLine() throws IOException, URISyntaxException {
261+
final String CLASS_NAME = "CapturedSnapshot02";
262+
int line = getLineForLineProbe(CLASS_NAME, LINE_PROBE_ID2);
263+
TestSnapshotListener listener = installLineProbe(LINE_PROBE_ID2, CLASS_NAME, line);
264+
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
265+
int result = Reflect.onClass(testClass).call("main", "f").get();
266+
assertEquals(42, result);
267+
ArgumentCaptor<ProbeId> probeIdCaptor = ArgumentCaptor.forClass(ProbeId.class);
268+
ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class);
269+
verify(probeStatusSink).addError(probeIdCaptor.capture(), strCaptor.capture());
270+
assertEquals(LINE_PROBE_ID2.getId(), probeIdCaptor.getAllValues().get(0).getId());
271+
assertEquals(
272+
"Cannot instrument the first line of a constructor", strCaptor.getAllValues().get(0));
273+
}
274+
259275
@Test
260276
public void overloadedConstructor() throws IOException, URISyntaxException {
261277
final String CLASS_NAME = "CapturedSnapshot02";

0 commit comments

Comments
 (0)