Skip to content

Commit fb48af6

Browse files
committed
Restore app context attributes wiped by ddprof setContext on span activation
1 parent ee7f3cc commit fb48af6

9 files changed

Lines changed: 674 additions & 13 deletions

File tree

‎dd-java-agent/agent-profiling/profiling-ddprof/build.gradle‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
plugins {
22
id 'com.gradleup.shadow'
3+
id 'me.champeau.jmh'
34
}
45

56
ext {
@@ -41,6 +42,19 @@ dependencies {
4142
}
4243

4344

45+
jmh {
46+
jmhVersion = libs.versions.jmh.get()
47+
duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE
48+
failOnError = false
49+
forceGC = true
50+
if (project.hasProperty('jmhIncludes')) {
51+
includes = [project.jmhIncludes]
52+
}
53+
if (project.hasProperty('jmhProf')) {
54+
profilers = [project.jmhProf]
55+
}
56+
}
57+
4458
configurations.configureEach {
4559
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
4660
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.datadog.profiling.ddprof;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.util.concurrent.TimeUnit;
5+
import org.openjdk.jmh.annotations.Benchmark;
6+
import org.openjdk.jmh.annotations.BenchmarkMode;
7+
import org.openjdk.jmh.annotations.Fork;
8+
import org.openjdk.jmh.annotations.Measurement;
9+
import org.openjdk.jmh.annotations.Mode;
10+
import org.openjdk.jmh.annotations.OutputTimeUnit;
11+
import org.openjdk.jmh.annotations.Param;
12+
import org.openjdk.jmh.annotations.Scope;
13+
import org.openjdk.jmh.annotations.Setup;
14+
import org.openjdk.jmh.annotations.State;
15+
import org.openjdk.jmh.annotations.Warmup;
16+
17+
/**
18+
* Verifies that the ScopeStack pool approach (AppContextSnapshot.copyFrom into a pre-allocated
19+
* slot) is allocation-free on the save/restore hot path.
20+
*
21+
* <p>The {@code deepStack} benchmark uses {@code stackDepth=16} to trigger a one-time resize
22+
* (default pool size is 8) and confirm that subsequent iterations at that depth are still
23+
* allocation-free once the pool has grown.
24+
*
25+
* <p>Run with: ./gradlew :dd-java-agent:agent-profiling:profiling-ddprof:jmh
26+
* -PjmhIncludes=AppContextSnapshotBenchmark -PjmhProf=gc
27+
*
28+
* <p>Expected: gc.alloc.rate.norm ≈ 0 B/op for save, restore, and deepStack (steady state).
29+
*/
30+
@BenchmarkMode(Mode.AverageTime)
31+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
32+
@Warmup(iterations = 3, time = 1)
33+
@Measurement(iterations = 5, time = 1)
34+
@Fork(1)
35+
@State(Scope.Thread)
36+
public class AppContextSnapshotBenchmark {
37+
38+
@Param({"2", "8"})
39+
int attrCount;
40+
41+
/**
42+
* Stack depth for the deepStack benchmark — 16 forces one resize past the default 8-slot pool.
43+
*/
44+
@Param({"8", "16"})
45+
int stackDepth;
46+
47+
private DatadogProfiler.AppContextSnapshot source;
48+
private DatadogProfiler.AppContextSnapshot slot;
49+
private DatadogProfiler.ScopeStack stack;
50+
51+
@Setup
52+
public void setup() {
53+
source = new DatadogProfiler.AppContextSnapshot(attrCount);
54+
for (int i = 0; i < attrCount; i++) {
55+
byte[] utf8 = ("value-" + i).getBytes(StandardCharsets.UTF_8);
56+
source.record(i, i + 1, utf8, "value-" + i);
57+
}
58+
slot = new DatadogProfiler.AppContextSnapshot(attrCount);
59+
stack = new DatadogProfiler.ScopeStack(attrCount);
60+
}
61+
62+
/** ScopeStack save: copies current snapshot into a pre-allocated pool slot (zero alloc). */
63+
@Benchmark
64+
public void save() {
65+
slot.copyFrom(source);
66+
}
67+
68+
/** ScopeStack restore: copies pool slot back into the live snapshot (zero alloc). */
69+
@Benchmark
70+
public void restore() {
71+
source.copyFrom(slot);
72+
}
73+
74+
/**
75+
* Borrow {@code stackDepth} slots then release them all. At {@code stackDepth=16} the pool
76+
* resizes during warmup; steady-state measurement confirms zero allocation after growth.
77+
*/
78+
@Benchmark
79+
public void deepStack() {
80+
for (int i = 0; i < stackDepth; i++) {
81+
stack.borrow().copyFrom(source);
82+
}
83+
for (int i = 0; i < stackDepth; i++) {
84+
stack.release();
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)