33import static org .junit .jupiter .api .Assertions .assertEquals ;
44import static org .junit .jupiter .api .Assertions .assertFalse ;
55import static org .junit .jupiter .api .Assertions .assertNotNull ;
6- import static org .junit .jupiter .api .Assertions .assertNull ;
6+ import static org .junit .jupiter .api .Assertions .assertTrue ;
77
88import com .datadog .debugger .sink .Snapshot ;
99import datadog .trace .api .Platform ;
1010import datadog .trace .bootstrap .debugger .CapturedContext ;
1111import datadog .trace .test .agent .decoder .DecodedSpan ;
1212import datadog .trace .test .agent .decoder .DecodedTrace ;
1313import java .nio .file .Path ;
14+ import java .util .ArrayList ;
1415import java .util .HashMap ;
1516import java .util .List ;
1617import java .util .Map ;
2021
2122public class ExceptionDebuggerIntegrationTest extends ServerAppDebuggerIntegrationTest {
2223
23- private String snapshotId0 ;
24- private String snapshotId1 ;
25- private String snapshotId2 ;
24+ private List <String > snapshotIdTags = new ArrayList <>();
2625 private boolean traceReceived ;
2726 private boolean snapshotReceived ;
2827 private Map <String , Snapshot > snapshots = new HashMap <>();
28+ private List <String > additionalJvmArgs = new ArrayList <>();
2929
3030 @ Override
3131 protected ProcessBuilder createProcessBuilder (Path logFilePath , String ... params ) {
@@ -36,6 +36,7 @@ protected ProcessBuilder createProcessBuilder(Path logFilePath, String... params
3636 commandParams .add ("-Ddd.third.party.excludes=datadog.smoketest" );
3737 // disable DI to make sure exception debugger works alone
3838 commandParams .remove ("-Ddd.dynamic.instrumentation.enabled=true" );
39+ commandParams .addAll (additionalJvmArgs );
3940 return ProcessBuilderHelper .createProcessBuilder (
4041 commandParams , logFilePath , getAppClass (), params );
4142 }
@@ -46,13 +47,18 @@ protected ProcessBuilder createProcessBuilder(Path logFilePath, String... params
4647 value = "datadog.trace.api.Platform#isJ9" ,
4748 disabledReason = "we cannot get local variable debug info" )
4849 void testSimpleSingleFrameException () throws Exception {
50+ appUrl = startAppAndAndGetUrl ();
4951 execute (appUrl , TRACED_METHOD_NAME , "oops" ); // instrumenting first exception
5052 waitForInstrumentation (appUrl );
5153 execute (appUrl , TRACED_METHOD_NAME , "oops" ); // collecting snapshots and sending them
5254 registerTraceListener (this ::receiveExceptionReplayTrace );
5355 registerSnapshotListener (this ::receiveSnapshot );
5456 processRequests (
5557 () -> {
58+ if (snapshotIdTags .isEmpty ()) {
59+ return false ;
60+ }
61+ String snapshotId0 = snapshotIdTags .get (0 );
5662 if (traceReceived && snapshotReceived && snapshots .containsKey (snapshotId0 )) {
5763 Snapshot snapshot = snapshots .get (snapshotId0 );
5864 assertNotNull (snapshot );
@@ -71,6 +77,7 @@ void testSimpleSingleFrameException() throws Exception {
7177 value = "datadog.trace.api.Platform#isJ9" ,
7278 disabledReason = "we cannot get local variable debug info" )
7379 void testNoSubsequentCaptureAfterFirst () throws Exception {
80+ appUrl = startAppAndAndGetUrl ();
7481 testSimpleSingleFrameException ();
7582 resetSnapshotsAndTraces ();
7683 // we should not receive any more snapshots after the first one
@@ -88,39 +95,40 @@ void testNoSubsequentCaptureAfterFirst() throws Exception {
8895 processRequests (() -> traceReceived && !snapshotReceived );
8996 }
9097
98+ // DeepOops exception stacktrace:
99+ // java.lang.RuntimeException: oops
100+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithException(ServerDebuggerTestApplication.java:190)
101+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException5(ServerDebuggerTestApplication.java:210)
102+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException4(ServerDebuggerTestApplication.java:206)
103+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException3(ServerDebuggerTestApplication.java:202)
104+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException2(ServerDebuggerTestApplication.java:198)
105+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException1(ServerDebuggerTestApplication.java:194)
106+ // datadog.smoketest.debugger.ServerDebuggerTestApplication.runTracedMethod(ServerDebuggerTestApplication.java:140)
91107 @ Test
92108 @ DisplayName ("test3CapturedFrames" )
93109 @ DisabledIf (
94110 value = "datadog.trace.api.Platform#isJ9" ,
95111 disabledReason = "we cannot get local variable debug info" )
96112 void test3CapturedFrames () throws Exception {
113+ appUrl = startAppAndAndGetUrl ();
97114 execute (appUrl , TRACED_METHOD_NAME , "deepOops" ); // instrumenting first exception
98115 waitForInstrumentation (appUrl );
99116 execute (appUrl , TRACED_METHOD_NAME , "deepOops" ); // collecting snapshots and sending them
100117 registerTraceListener (this ::receiveExceptionReplayTrace );
101118 registerSnapshotListener (this ::receiveSnapshot );
102119 processRequests (
103120 () -> {
121+ if (snapshotIdTags .isEmpty ()) {
122+ return false ;
123+ }
124+ String snapshotId0 = snapshotIdTags .get (0 );
125+ String snapshotId1 = snapshotIdTags .get (1 );
126+ String snapshotId2 = snapshotIdTags .get (2 );
104127 if (traceReceived
105128 && snapshotReceived
106129 && snapshots .containsKey (snapshotId0 )
107130 && snapshots .containsKey (snapshotId1 )
108131 && snapshots .containsKey (snapshotId2 )) {
109- // java.lang.RuntimeException: oops
110- // at
111- // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithException(ServerDebuggerTestApplication.java:190)
112- // at
113- // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException5(ServerDebuggerTestApplication.java:210)
114- // at
115- // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException4(ServerDebuggerTestApplication.java:206)
116- // at
117- // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException3(ServerDebuggerTestApplication.java:202)
118- // at
119- // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException2(ServerDebuggerTestApplication.java:198)
120- // at
121- // datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException1(ServerDebuggerTestApplication.java:194)
122- // at
123- // datadog.smoketest.debugger.ServerDebuggerTestApplication.runTracedMethod(ServerDebuggerTestApplication.java:140)
124132 // snapshot 0
125133 Snapshot snapshot = snapshots .get (snapshotId0 );
126134 assertNotNull (snapshot );
@@ -152,14 +160,89 @@ void test3CapturedFrames() throws Exception {
152160 });
153161 }
154162
163+ @ Test
164+ @ DisplayName ("test5CapturedFrames" )
165+ @ DisabledIf (
166+ value = "datadog.trace.api.Platform#isJ9" ,
167+ disabledReason = "we cannot get local variable debug info" )
168+ void test5CapturedFrames () throws Exception {
169+ additionalJvmArgs .add ("-Ddd.exception.replay.capture.max.frames=5" );
170+ appUrl = startAppAndAndGetUrl ();
171+ execute (appUrl , TRACED_METHOD_NAME , "deepOops" ); // instrumenting first exception
172+ waitForInstrumentation (appUrl );
173+ execute (appUrl , TRACED_METHOD_NAME , "deepOops" ); // collecting snapshots and sending them
174+ registerTraceListener (this ::receiveExceptionReplayTrace );
175+ registerSnapshotListener (this ::receiveSnapshot );
176+ processRequests (
177+ () -> {
178+ if (snapshotIdTags .isEmpty ()) {
179+ return false ;
180+ }
181+ String snapshotId0 = snapshotIdTags .get (0 );
182+ String snapshotId1 = snapshotIdTags .get (1 );
183+ String snapshotId2 = snapshotIdTags .get (2 );
184+ String snapshotId3 = snapshotIdTags .get (3 );
185+ String snapshotId4 = snapshotIdTags .get (4 );
186+ if (traceReceived
187+ && snapshotReceived
188+ && snapshots .containsKey (snapshotId0 )
189+ && snapshots .containsKey (snapshotId1 )
190+ && snapshots .containsKey (snapshotId2 )
191+ && snapshots .containsKey (snapshotId3 )
192+ && snapshots .containsKey (snapshotId4 )) {
193+ // snapshot 0
194+ Snapshot snapshot = snapshots .get (snapshotId0 );
195+ assertNotNull (snapshot );
196+ assertEquals (
197+ "oops" , snapshot .getCaptures ().getReturn ().getCapturedThrowable ().getMessage ());
198+ assertEquals (
199+ "datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithException" ,
200+ snapshot .getStack ().get (0 ).getFunction ());
201+ assertFullMethodCaptureArgs (snapshot .getCaptures ().getReturn ());
202+ // snapshot 1
203+ snapshot = snapshots .get (snapshotId1 );
204+ assertEquals (
205+ "oops" , snapshot .getCaptures ().getReturn ().getCapturedThrowable ().getMessage ());
206+ assertEquals (
207+ "datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException5" ,
208+ snapshot .getStack ().get (0 ).getFunction ());
209+ assertFullMethodCaptureArgs (snapshot .getCaptures ().getReturn ());
210+ // snapshot 2
211+ snapshot = snapshots .get (snapshotId2 );
212+ assertEquals (
213+ "oops" , snapshot .getCaptures ().getReturn ().getCapturedThrowable ().getMessage ());
214+ assertEquals (
215+ "datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException4" ,
216+ snapshot .getStack ().get (0 ).getFunction ());
217+ assertFullMethodCaptureArgs (snapshot .getCaptures ().getReturn ());
218+ // snapshot 3
219+ snapshot = snapshots .get (snapshotId3 );
220+ assertEquals (
221+ "oops" , snapshot .getCaptures ().getReturn ().getCapturedThrowable ().getMessage ());
222+ assertEquals (
223+ "datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException3" ,
224+ snapshot .getStack ().get (0 ).getFunction ());
225+ assertFullMethodCaptureArgs (snapshot .getCaptures ().getReturn ());
226+ // snapshot 4
227+ snapshot = snapshots .get (snapshotId4 );
228+ assertEquals (
229+ "oops" , snapshot .getCaptures ().getReturn ().getCapturedThrowable ().getMessage ());
230+ assertEquals (
231+ "datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithDeepException2" ,
232+ snapshot .getStack ().get (0 ).getFunction ());
233+ assertFullMethodCaptureArgs (snapshot .getCaptures ().getReturn ());
234+ return true ;
235+ }
236+ return false ;
237+ });
238+ }
239+
155240 private void resetSnapshotsAndTraces () {
156241 resetTraceListener ();
157242 traceReceived = false ;
158243 snapshotReceived = false ;
159244 snapshots .clear ();
160- snapshotId0 = null ;
161- snapshotId1 = null ;
162- snapshotId2 = null ;
245+ snapshotIdTags .clear ();
163246 }
164247
165248 private void assertFullMethodCaptureArgs (CapturedContext context ) {
@@ -178,10 +261,13 @@ private void receiveExceptionReplayTrace(DecodedTrace decodedTrace) {
178261 for (DecodedSpan span : decodedTrace .getSpans ()) {
179262 if (isTracedFullMethodSpan (span ) && span .getMeta ().containsKey ("error.debug_info_captured" )) {
180263 // assert that we have received the trace with ER tags only once
181- assertNull (snapshotId0 );
182- snapshotId0 = span .getMeta ().get ("_dd.debug.error.0.snapshot_id" );
183- snapshotId1 = span .getMeta ().get ("_dd.debug.error.1.snapshot_id" );
184- snapshotId2 = span .getMeta ().get ("_dd.debug.error.2.snapshot_id" );
264+ assertTrue (snapshotIdTags .isEmpty ());
265+ for (int i = 0 ; i < 5 ; i ++) {
266+ String snapshotId = span .getMeta ().get ("_dd.debug.error." + i + ".snapshot_id" );
267+ if (snapshotId != null ) {
268+ snapshotIdTags .add (snapshotId );
269+ }
270+ }
185271 assertFalse (traceReceived );
186272 traceReceived = true ;
187273 }
0 commit comments