1+ package datadog .trace .bootstrap .instrumentation .api ;
2+
3+ import static org .junit .jupiter .api .Assertions .assertEquals ;
4+
5+ import java .nio .file .Files ;
6+ import java .nio .file .Path ;
7+ import java .time .Duration ;
8+ import java .util .ArrayList ;
9+ import java .util .List ;
10+ import jdk .jfr .Recording ;
11+ import jdk .jfr .consumer .RecordedEvent ;
12+ import jdk .jfr .consumer .RecordedFrame ;
13+ import jdk .jfr .consumer .RecordedMethod ;
14+ import jdk .jfr .consumer .RecordedStackTrace ;
15+ import jdk .jfr .consumer .RecordingFile ;
16+ import org .junit .jupiter .api .Test ;
17+ import org .junit .jupiter .api .condition .EnabledForJreRange ;
18+ import org .junit .jupiter .api .condition .JRE ;
19+ import org .moditect .jfrunit .JfrEventTest ;
20+
21+ @ JfrEventTest
22+ @ EnabledForJreRange (min = JRE .JAVA_11 )
23+ class AgentTracerSpanCreationMemoryTest {
24+ private static final String INSTRUMENTATION_NAME = "memory-test" ;
25+ private static final CharSequence SPAN_NAME = "span-creation" ;
26+
27+ @ Test
28+ void startSpanDoesNotAllocateInNoopMode () throws Exception {
29+ warmupSpanCreation ();
30+
31+ List <RecordedEvent > events = recordAllocationsWhileCreatingSpans ();
32+ long allocationEventsOnStartSpan = events .stream ().filter (this ::isStartSpanAllocation ).count ();
33+
34+ assertEquals (0L , allocationEventsOnStartSpan );
35+ }
36+
37+ private static void warmupSpanCreation () {
38+ for (int i = 0 ; i < 10_000 ; i ++) {
39+ createSpan ();
40+ }
41+ }
42+
43+ private static List <RecordedEvent > recordAllocationsWhileCreatingSpans () throws Exception {
44+ try (Recording recording = new Recording ()) {
45+ recording .enable ("jdk.ObjectAllocationInNewTLAB" ).withStackTrace ().withThreshold (Duration .ZERO );
46+ recording .enable ("jdk.ObjectAllocationOutsideTLAB" ).withStackTrace ().withThreshold (Duration .ZERO );
47+ recording .start ();
48+ for (int i = 0 ; i < 25_000 ; i ++) {
49+ createSpan ();
50+ }
51+ recording .stop ();
52+ return readEvents (recording );
53+ }
54+ }
55+
56+ private static void createSpan () {
57+ AgentSpan span = AgentTracer .startSpan (INSTRUMENTATION_NAME , SPAN_NAME );
58+ span .finish ();
59+ }
60+
61+ private static List <RecordedEvent > readEvents (Recording recording ) throws Exception {
62+ Path recordingPath = Files .createTempFile ("agent-tracer-span-memory-test" , ".jfr" );
63+ try {
64+ recording .dump (recordingPath );
65+ List <RecordedEvent > events = new ArrayList <>();
66+ try (RecordingFile recordingFile = new RecordingFile (recordingPath )) {
67+ while (recordingFile .hasMoreEvents ()) {
68+ events .add (recordingFile .readEvent ());
69+ }
70+ }
71+ return events ;
72+ } finally {
73+ Files .deleteIfExists (recordingPath );
74+ }
75+ }
76+
77+ private boolean isStartSpanAllocation (RecordedEvent event ) {
78+ RecordedStackTrace stackTrace = event .getStackTrace ();
79+ if (stackTrace == null ) {
80+ return false ;
81+ }
82+
83+ for (RecordedFrame frame : stackTrace .getFrames ()) {
84+ RecordedMethod method = frame .getMethod ();
85+ if (method == null || method .getType () == null ) {
86+ continue ;
87+ }
88+
89+ if ("datadog.trace.bootstrap.instrumentation.api.AgentTracer" .equals (method .getType ().getName ())
90+ && "startSpan" .equals (method .getName ())) {
91+ return true ;
92+ }
93+ }
94+ return false ;
95+ }
96+ }
0 commit comments