Skip to content

Commit 24fec34

Browse files
committed
test(wall): add TaskBlock and combined precheck/park coverage
1 parent 54d15f0 commit 24fec34

2 files changed

Lines changed: 190 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.datadoghq.profiler.wallclock;
2+
3+
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
5+
import com.datadoghq.profiler.AbstractProfilerTest;
6+
import com.datadoghq.profiler.Platform;
7+
import org.junit.jupiter.api.Assumptions;
8+
import org.junit.jupiter.api.Test;
9+
import org.openjdk.jmc.common.item.Aggregators;
10+
import org.openjdk.jmc.common.item.IItemCollection;
11+
12+
import java.util.Map;
13+
14+
/**
15+
* Approach B integration test: parkEnter/parkExit must emit TaskBlock and suppress wall-clock
16+
* signals while parked (via ProfiledThread flag when thread filter lists the thread).
17+
*/
18+
public class ParkTaskBlockTest extends AbstractProfilerTest {
19+
20+
/** Verifies TaskBlock emission and parked signal suppression on the same thread. */
21+
@Test
22+
public void parkIntervalEmitsTaskBlockAndSuppressesSignals() {
23+
Assumptions.assumeTrue(!Platform.isJ9());
24+
registerCurrentThreadForWallClockProfiling();
25+
26+
long spanId = 0x1234L;
27+
long rootSpanId = 0x5678L;
28+
profiler.setContext(rootSpanId, spanId, 0, 0);
29+
30+
long parkUntil = System.nanoTime() + 250_000_000L; // 250 ms
31+
profiler.parkEnter(spanId, rootSpanId);
32+
while (System.nanoTime() < parkUntil) {
33+
// Deliberately stay runnable while "parked": suppression must come from park flag,
34+
// not from sleeping-state precheck.
35+
}
36+
profiler.parkExit(System.identityHashCode(this), 0L);
37+
profiler.clearContext();
38+
39+
// Keep profiler active after park interval so regular wall samples still occur.
40+
long activeUntil = System.nanoTime() + 120_000_000L;
41+
while (System.nanoTime() < activeUntil) {
42+
// busy
43+
}
44+
45+
stopProfiler();
46+
47+
IItemCollection taskBlocks = verifyEvents("datadog.TaskBlock");
48+
long taskBlockCount = taskBlocks.getAggregate(Aggregators.count()).longValue();
49+
assertTrue(taskBlockCount > 0, "Expected datadog.TaskBlock events after parkEnter/parkExit");
50+
51+
IItemCollection methodSamples = verifyEvents("datadog.MethodSample");
52+
long methodSampleCount = methodSamples.getAggregate(Aggregators.count()).longValue();
53+
assertTrue(methodSampleCount > 0, "Expected MethodSample events outside the parked interval");
54+
55+
Map<String, Long> counters = profiler.getDebugCounters();
56+
if (counters.containsKey("wc_signals_skipped_parked")) {
57+
assertTrue(
58+
counters.get("wc_signals_skipped_parked") > 0,
59+
"Expected wc_signals_skipped_parked > 0");
60+
}
61+
}
62+
63+
/** Configures wall-clock profiling with precheck disabled to isolate parked-flag behavior. */
64+
@Override
65+
protected String getProfilerCommand() {
66+
return "wall=1ms,filter=0,wallprecheck=false";
67+
}
68+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.datadoghq.profiler.wallclock;
2+
3+
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
5+
import com.datadoghq.profiler.AbstractProfilerTest;
6+
import com.datadoghq.profiler.Platform;
7+
import org.junit.jupiter.api.Assumptions;
8+
import org.junit.jupiter.api.Test;
9+
import org.openjdk.jmc.common.item.Aggregators;
10+
import org.openjdk.jmc.common.item.IItemCollection;
11+
12+
import java.util.Map;
13+
import java.util.concurrent.CountDownLatch;
14+
import java.util.concurrent.atomic.AtomicBoolean;
15+
16+
/**
17+
* Combined Approach A + B integration test:
18+
* - Approach A (precheck): sleeping thread should be skipped.
19+
* - Approach B (park flag): parked runnable thread should be skipped and produce TaskBlock.
20+
* - Correctness guard: runnable thread still produces MethodSample events.
21+
*/
22+
public class WallclockMitigationsCombinedTest extends AbstractProfilerTest {
23+
24+
/** Verifies Approach A and B counters/events can be observed concurrently. */
25+
@Test
26+
public void precheckAndParkSuppressionWorkTogether() throws Exception {
27+
Assumptions.assumeTrue(!Platform.isJ9());
28+
Assumptions.assumeTrue(
29+
Platform.isJavaVersionAtLeast(11),
30+
"Sleeping-state precheck assertions are stable on JDK 11+");
31+
32+
CountDownLatch ready = new CountDownLatch(3);
33+
AtomicBoolean stop = new AtomicBoolean(false);
34+
35+
Thread sleeping =
36+
new Thread(
37+
() -> {
38+
registerCurrentThreadForWallClockProfiling();
39+
ready.countDown();
40+
try {
41+
Thread.sleep(280);
42+
} catch (InterruptedException ignored) {
43+
}
44+
},
45+
"combined-sleeping");
46+
47+
Thread parkedBusy =
48+
new Thread(
49+
() -> {
50+
registerCurrentThreadForWallClockProfiling();
51+
long spanId = 0x1111L;
52+
long rootSpanId = 0x2222L;
53+
profiler.setContext(rootSpanId, spanId, 0, 0);
54+
ready.countDown();
55+
profiler.parkEnter(spanId, rootSpanId);
56+
long parkedUntil = System.nanoTime() + 280_000_000L;
57+
while (System.nanoTime() < parkedUntil) {
58+
// Runnable while flagged parked.
59+
}
60+
profiler.parkExit(System.identityHashCode(this), 0L);
61+
profiler.clearContext();
62+
},
63+
"combined-parked");
64+
65+
Thread runnable =
66+
new Thread(
67+
() -> {
68+
registerCurrentThreadForWallClockProfiling();
69+
ready.countDown();
70+
while (!stop.get()) {
71+
// keep runnable
72+
}
73+
},
74+
"combined-runnable");
75+
76+
sleeping.setDaemon(true);
77+
parkedBusy.setDaemon(true);
78+
runnable.setDaemon(true);
79+
sleeping.start();
80+
parkedBusy.start();
81+
runnable.start();
82+
83+
ready.await();
84+
Thread.sleep(350);
85+
stop.set(true);
86+
87+
sleeping.interrupt();
88+
sleeping.join(1000);
89+
parkedBusy.join(1000);
90+
runnable.join(1000);
91+
92+
stopProfiler();
93+
94+
IItemCollection taskBlocks = verifyEvents("datadog.TaskBlock");
95+
assertTrue(
96+
taskBlocks.getAggregate(Aggregators.count()).longValue() > 0,
97+
"Expected TaskBlock events from parked interval");
98+
99+
IItemCollection methodSamples = verifyEvents("datadog.MethodSample");
100+
assertTrue(
101+
methodSamples.getAggregate(Aggregators.count()).longValue() > 0,
102+
"Expected runnable MethodSample events while mitigations are enabled");
103+
104+
Map<String, Long> counters = profiler.getDebugCounters();
105+
if (counters.containsKey("wc_signals_skipped_sleeping")) {
106+
assertTrue(
107+
counters.get("wc_signals_skipped_sleeping") > 0,
108+
"Expected sleeping precheck counter to increase");
109+
}
110+
if (counters.containsKey("wc_signals_skipped_parked")) {
111+
assertTrue(
112+
counters.get("wc_signals_skipped_parked") > 0,
113+
"Expected parked suppression counter to increase");
114+
}
115+
}
116+
117+
/** Enables wall-clock profiling with default precheck behavior for combined assertions. */
118+
@Override
119+
protected String getProfilerCommand() {
120+
return "wall=1ms,filter=0";
121+
}
122+
}

0 commit comments

Comments
 (0)