|
1 | 1 | package com.datadog.debugger.exception; |
2 | 2 |
|
| 3 | +import static com.datadog.debugger.util.TestHelper.assertWithTimeout; |
| 4 | +import static java.util.Arrays.asList; |
3 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; |
4 | 6 | import static org.junit.jupiter.api.Assertions.assertFalse; |
5 | 7 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 8 | +import static org.mockito.Mockito.doAnswer; |
6 | 9 | import static org.mockito.Mockito.mock; |
7 | 10 | import static org.mockito.Mockito.when; |
8 | 11 |
|
9 | 12 | import com.datadog.debugger.probe.ExceptionProbe; |
10 | 13 | import com.datadog.debugger.util.ClassNameFiltering; |
11 | 14 | import datadog.trace.api.Config; |
| 15 | +import datadog.trace.bootstrap.debugger.CapturedContext; |
| 16 | +import datadog.trace.bootstrap.debugger.MethodLocation; |
12 | 17 | import java.time.Clock; |
13 | 18 | import java.time.Duration; |
14 | 19 | import java.time.Instant; |
15 | 20 | import java.util.Collections; |
| 21 | +import java.util.HashSet; |
| 22 | +import java.util.Set; |
| 23 | +import java.util.concurrent.atomic.AtomicBoolean; |
16 | 24 | import java.util.stream.Collectors; |
17 | 25 | import java.util.stream.Stream; |
| 26 | +import org.jetbrains.annotations.NotNull; |
18 | 27 | import org.junit.jupiter.api.Test; |
19 | 28 |
|
20 | 29 | class ExceptionProbeManagerTest { |
| 30 | + private static final @NotNull Set<String> FILTERED_PACKAGES = |
| 31 | + new HashSet<>( |
| 32 | + asList( |
| 33 | + "java.", |
| 34 | + "jdk.", |
| 35 | + "sun.", |
| 36 | + "com.sun.", |
| 37 | + "org.gradle.", |
| 38 | + "worker.org.gradle.", |
| 39 | + "org.junit.")); |
21 | 40 | private final RuntimeException exception = new RuntimeException("test"); |
22 | 41 |
|
23 | 42 | @Test |
24 | 43 | public void instrumentStackTrace() { |
25 | 44 | ClassNameFiltering classNameFiltering = ClassNameFiltering.allowAll(); |
26 | 45 | ExceptionProbeManager exceptionProbeManager = new ExceptionProbeManager(classNameFiltering); |
27 | 46 | RuntimeException exception = new RuntimeException("test"); |
28 | | - String fingerprint = Fingerprinter.fingerprint(exception, classNameFiltering); |
29 | 47 | exceptionProbeManager.createProbesForException(exception.getStackTrace(), 0); |
30 | 48 | assertFalse(exceptionProbeManager.getProbes().isEmpty()); |
31 | 49 | } |
32 | 50 |
|
33 | 51 | @Test |
34 | 52 | void instrumentSingleFrame() { |
35 | | - ClassNameFiltering classNameFiltering = |
36 | | - new ClassNameFiltering( |
37 | | - Stream.of( |
38 | | - "java.", |
39 | | - "jdk.", |
40 | | - "sun.", |
41 | | - "com.sun.", |
42 | | - "org.gradle.", |
43 | | - "worker.org.gradle.", |
44 | | - "org.junit.") |
45 | | - .collect(Collectors.toSet())); |
| 53 | + ClassNameFiltering classNameFiltering = new ClassNameFiltering(FILTERED_PACKAGES); |
46 | 54 | ExceptionProbeManager exceptionProbeManager = new ExceptionProbeManager(classNameFiltering); |
47 | 55 |
|
48 | 56 | String fingerprint = Fingerprinter.fingerprint(exception, classNameFiltering); |
49 | | - assertEquals("4974b2b4853e6152d8f218fb79a42a761a45335447e22e53d75f5325e742655", fingerprint); |
| 57 | + assertEquals("44cbdcfe32eea21c3523e4a5885daabf5b8c9428cc0f613be5ff29663465ff9", fingerprint); |
50 | 58 | exceptionProbeManager.createProbesForException(exception.getStackTrace(), 0); |
51 | 59 | assertEquals(1, exceptionProbeManager.getProbes().size()); |
52 | 60 | ExceptionProbe exceptionProbe = exceptionProbeManager.getProbes().iterator().next(); |
@@ -82,34 +90,53 @@ void lastCapture() { |
82 | 90 | RuntimeException exception = new RuntimeException("test"); |
83 | 91 | String fingerprint = Fingerprinter.fingerprint(exception, classNameFiltering); |
84 | 92 | exceptionProbeManager.addFingerprint(fingerprint); |
85 | | - assertTrue(exceptionProbeManager.shouldCaptureException(fingerprint)); |
| 93 | + Instant lastCapture = exceptionProbeManager.getLastCapture(fingerprint); |
| 94 | + assertTrue(exceptionProbeManager.shouldCaptureException(lastCapture)); |
86 | 95 | exceptionProbeManager.updateLastCapture(fingerprint); |
87 | | - assertFalse(exceptionProbeManager.shouldCaptureException(fingerprint)); |
| 96 | + lastCapture = exceptionProbeManager.getLastCapture(fingerprint); |
| 97 | + assertFalse(exceptionProbeManager.shouldCaptureException(lastCapture)); |
88 | 98 | Clock clock = |
89 | 99 | Clock.fixed(Instant.now().plus(Duration.ofMinutes(61)), Clock.systemUTC().getZone()); |
90 | | - assertTrue(exceptionProbeManager.shouldCaptureException(fingerprint, clock)); |
| 100 | + lastCapture = exceptionProbeManager.getLastCapture(fingerprint); |
| 101 | + assertTrue(exceptionProbeManager.shouldCaptureException(lastCapture, clock)); |
91 | 102 | } |
92 | 103 |
|
93 | 104 | @Test |
94 | 105 | void maxFrames() { |
95 | 106 | RuntimeException deepException = level1(); |
96 | | - ClassNameFiltering classNameFiltering = |
97 | | - new ClassNameFiltering( |
98 | | - Stream.of( |
99 | | - "java.", |
100 | | - "jdk.", |
101 | | - "sun.", |
102 | | - "com.sun.", |
103 | | - "org.gradle.", |
104 | | - "worker.org.gradle.", |
105 | | - "org.junit.") |
106 | | - .collect(Collectors.toSet())); |
| 107 | + ClassNameFiltering classNameFiltering = new ClassNameFiltering(FILTERED_PACKAGES); |
107 | 108 | ExceptionProbeManager exceptionProbeManager = |
108 | 109 | new ExceptionProbeManager(classNameFiltering, Duration.ofHours(1), Clock.systemUTC(), 3); |
109 | 110 | exceptionProbeManager.createProbesForException(deepException.getStackTrace(), 0); |
110 | 111 | assertEquals(3, exceptionProbeManager.getProbes().size()); |
111 | 112 | } |
112 | 113 |
|
| 114 | + @Test |
| 115 | + public void removeExceptionProbeOnHotLoop() { |
| 116 | + ClassNameFiltering classNameFiltering = new ClassNameFiltering(FILTERED_PACKAGES); |
| 117 | + ExceptionProbeManager exceptionProbeManager = new ExceptionProbeManager(classNameFiltering); |
| 118 | + AtomicBoolean configApplied = new AtomicBoolean(false); |
| 119 | + DefaultExceptionDebugger defaultExceptionDebugger = mock(DefaultExceptionDebugger.class); |
| 120 | + doAnswer( |
| 121 | + invocationOnMock -> { |
| 122 | + configApplied.set(true); |
| 123 | + return null; |
| 124 | + }) |
| 125 | + .when(defaultExceptionDebugger) |
| 126 | + .applyExceptionConfiguration(); |
| 127 | + exceptionProbeManager.setDefaultExceptionDebugger(defaultExceptionDebugger); |
| 128 | + exceptionProbeManager.createProbesForException(exception.getStackTrace(), 0); |
| 129 | + assertEquals(1, exceptionProbeManager.getProbes().size()); |
| 130 | + ExceptionProbe exceptionProbe = exceptionProbeManager.getProbes().iterator().next(); |
| 131 | + // simulate a code hot loop |
| 132 | + for (int i = 0; i < 2000; i++) { |
| 133 | + CapturedContext context = mock(CapturedContext.class); |
| 134 | + exceptionProbe.evaluate(context, exceptionProbe.createStatus(), MethodLocation.EXIT); |
| 135 | + } |
| 136 | + assertEquals(0, exceptionProbeManager.getProbes().size()); |
| 137 | + assertWithTimeout(configApplied::get, Duration.ofSeconds(30)); |
| 138 | + } |
| 139 | + |
113 | 140 | RuntimeException level1() { |
114 | 141 | return level2(); |
115 | 142 | } |
|
0 commit comments