Ensure JDI test VM cleanup after failed startup - #992
Open
carstenartur wants to merge 6 commits into
Open
carstenartur wants to merge 6 commits into
carstenartur wants to merge 6 commits into
Conversation
Clean up VM/proxy processes and readers when launch, attach or program startup fails, including the path used directly by VirtualMachineTest. Make shutdown safe before the event reader exists and preserve the startup failure when cleanup also fails. Wait for owned processes after normal and forced termination using bounded waits, retain interruption, and attempt proxy cleanup even if VM termination fails. Reset test state and select the next automatic port after failed startup, preserving custom VM command ports. Add 15 regression tests to the JDI AutomatedSuite, including injected lifecycle failures, process termination test doubles and a real Java subprocess. Leave the connection timeout and PR eclipse-jdt#990 unchanged. Signed-off-by: Carsten Hammer <carsten.hammer@t-online.de>
carstenartur
added a commit
to carstenartur/eclipse.jdt.debug
that referenced
this pull request
Sep 5, 2026
Compare the unchanged PR eclipse-jdt#992 commit with its upstream parent using three complete JDI AutomatedSuite runs on GitHub-hosted Ubuntu/JDK 21. Preserve Maven output, XML reports and process/socket observations. Fail when no real JDI tests ran; do not alter the upstream PR branch.
3 tasks
Retain the five-second startup budget, apply its remainder to each attach, and retain the transport cause and pre-cleanup process state in the failure. The unchanged silent-peer regressions fail on the prior cleanup-only commit and pass with this change; all 15 existing cleanup tests and the complete JDI suite also pass. Signed-off-by: Carsten Hammer <carsten.hammer@t-online.de>
carstenartur
added a commit
to carstenartur/eclipse.jdt.debug
that referenced
this pull request
Sep 5, 2026
Integrate df207ce as a separately reviewable prerequisite of the breakpoint-removal change in PR eclipse-jdt#990. The infrastructure commit passed upstream Jenkins PR-992 build 2, including 1474 tests and the overall build. Its JDI test bundle is copied byte-for-byte; the original seven breakpoint-change files are unchanged. Both changes share upstream base af15c26. This does not assert that the original connection-refused failures had the silent-peer cause. The combined change now needs its own upstream CI run. Merge eclipse-jdt#992 first; its commits remain distinguishable from the feature change, rather than being squashed into it. Signed-off-by: Carsten Hammer <carsten.hammer@t-online.de>
carstenartur
marked this pull request as ready for review
September 5, 2026 20:36
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
A small robustness bug in the new process-termination helper can throw UnsupportedOperationException from Process.pid() and mask the intended cleanup failure signal.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR hardens the JDI test harness against failed VM startup/attach/program-start scenarios by ensuring owned processes and reader threads are cleaned up even when setUp() aborts, and by bounding attach/termination waits to avoid hangs.
Changes:
- Add failure-safe startup cleanup in
AbstractJDITest(including interrupt preservation and diagnostic retention on connect timeouts). - Introduce bounded process termination helper and new regression tests covering injected failures and a real Java subprocess.
- Register the new regression tests in the JDI
AutomatedSuite.
File summaries
| File | Description |
|---|---|
| org.eclipse.jdt.debug.jdi.tests/tests/org/eclipse/debug/jdi/tests/VMStartupCleanupTest.java | Adds regression coverage for startup failure cleanup paths (processes, readers, ports, idempotent shutdown). |
| org.eclipse.jdt.debug.jdi.tests/tests/org/eclipse/debug/jdi/tests/VMConnectionTimeoutTest.java | Adds regression coverage ensuring a stalled JDWP handshake can’t hang startup and cleanup preserves diagnostics. |
| org.eclipse.jdt.debug.jdi.tests/tests/org/eclipse/debug/jdi/tests/TestProcessCleanup.java | Adds bounded termination utility used by the test harness to reliably stop owned processes. |
| org.eclipse.jdt.debug.jdi.tests/tests/org/eclipse/debug/jdi/tests/AutomatedSuite.java | Includes the new regression test classes in the automated suite. |
| org.eclipse.jdt.debug.jdi.tests/tests/org/eclipse/debug/jdi/tests/AbstractJDITest.java | Implements failure-safe cleanup on startup errors, bounded attach timeouts, and safer shutdown when readers/event reader may be absent. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Restore the class closing brace removed by the autofix and indent terminationFailure consistently. Keep the optional PID lookup guarded without changing process termination or timeout behavior.
Exercise Process.pid()'s default unsupported-handle path. Verify that cleanup still reports the termination failure, attempts every owned process, preserves suppressed failures and interruption, and does not replace the original startup failure. Keep the existing PID-supported cases and strengthen their diagnostic assertions alongside the new cases.
3 tasks
carstenartur
added a commit
to carstenartur/eclipse.jdt.debug
that referenced
this pull request
Sep 11, 2026
Keep the JDI test-infrastructure changes needed by this branch as one separately reviewable dependency snapshot. The shared harness cleans up VM/proxy processes and readers when launch, attach or program startup fails, uses bounded process termination, and bounds individual JDI attach attempts so startup cleanup remains reachable if a JDWP peer stalls during the handshake. These changes belong to eclipse-jdt#992 and are not part of the breakpoint-removal fix. After eclipse-jdt#992 is merged, eclipse-jdt#990 should be rebased onto the resulting upstream state so this dependency commit disappears from the final breakpoint-specific history. Signed-off-by: Carsten Hammer <carsten.hammer@t-online.de>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it does
Make the shared JDI test harness clean up owned VM/proxy processes after
failed launch, attach or program startup, and prevent a stalled JDWP
handshake from blocking the attach operation indefinitely.
The changes are confined to
org.eclipse.jdt.debug.jdi.tests. Theyaffect the test harness, not product-side debugger connection settings
or breakpoint behavior.
Related: #990.
This infrastructure change is reviewed separately from the
breakpoint-removal fix.
Why the shared infrastructure needs to change
Failed startup can bypass normal teardown
The harness can create processes and readers before VM startup has
completed. With the JUnit
TestCaselifecycle used here, an exceptionfrom
setUp()preventstearDown()from running:setUp()is calledbefore the
try/finallythat invokes teardown.See JUnit's
TestCase.runBare()implementation.Consequently, cleanup cannot rely exclusively on normal test teardown.
It must also run when the startup operation itself fails.
The existing shutdown path additionally assumes that the event reader
has already been created. That assumption does not hold when attaching
to the VM fails. Process destruction also previously returned without
waiting for process termination, so requesting cleanup did not establish
that the owned process had actually exited.
The cleanup belongs at the shared startup entry points rather than in
individual tests. This covers both the usual launch-and-start path and
callers of
launchTargetAndConnectToVM(), includingVirtualMachineTest,without duplicating failure handling across test classes.
A retry deadline does not bound a blocked attach call
The existing connection loop checks elapsed time between failed attach
attempts, but does not supply a timeout to the connector. A peer that
accepts the socket without completing the JDWP handshake can therefore
keep an individual attach attempt blocked, preventing both the retry
check and startup cleanup from being reached.
Cleanup alone cannot address that case. The connector call also needs
a finite timeout.
These are specific lifecycle and timeout defects. This PR does not
claim that they explain every earlier CI startup failure, or that
earlier connection-refused failures had the same cause as the
deliberately simulated silent-peer scenario.
Implementation
Exception-safe startup and shutdown
launchTargetAndConnectToVM()andlaunchTargetAndStartProgram()invokecleanup when their launch, attach or program-start operations throw a
RuntimeExceptionorError. The original failure is rethrown, and acleanup failure is attached as a suppressed exception rather than
replacing it.
Shutdown tolerates an absent event reader and attempts owned-process
cleanup in a
finallyblock, including when stopping readers orrequesting VM exit throws.
After cleanup, VM and reader references are cleared. Handles to
processes that are still alive are retained rather than discarded.
Repeated shutdown after successful cleanup does not terminate the
processes again or select another port.
The existing automatic port-selection mechanism is reused after
cleanup when target state was present. A user-supplied VM command keeps
its configured port.
Bounded process termination
The package-private
TestProcessCleanuphelper centralizes terminationof processes owned by the test harness.
For each live process, it requests normal termination and waits up to
five seconds. If necessary, it requests forced termination and waits
with a separate five-second deadline. Missing or already exited
processes are ignored.
Interruption does not abandon cleanup: the helper attempts forced
termination and restores the interrupt flag afterward. A failure for
one process does not prevent attempts to terminate the remaining
processes; additional failures are retained as suppressed exceptions.
A process that remains alive after forced termination produces an
IllegalStateException. Diagnostic formatting toleratesProcess.pid()throwingUnsupportedOperationException, reportingthe PID as unavailable without masking the intended cleanup failure.
Keeping this logic in a small helper allows the termination sequence,
failure aggregation and interrupt handling to be tested using process
test doubles, without depending on an overloaded or unresponsive JVM.
Attach timeout and diagnostic retention
The connection loop passes its remaining nominal five-second budget
to the connector instead of leaving the connector timeout unbounded.
Transport timeouts participate in the connection-failure handling.
Interruption detected before an attach attempt or during the retry
sleep aborts startup while preserving the interrupt flag.
When connection attempts fail, the reported error retains the last
connection failure as its cause. Additional diagnostics record the
port, VM/proxy process state and test-runtime information before
cleanup changes that state.
Behavioral impact and boundaries
This is a shared test-harness behavior change, not merely additional
regression tests.
The attach policy is stricter than before: an attempt that previously
blocked beyond the nominal retry budget can now fail with a timeout.
A slow test VM that previously connected only after exceeding that
budget may therefore fail sooner.
Normal test shutdown also changes. Owned-process termination is now
attempted after the VM exit request, and shutdown may wait for process
exit or escalate to forced termination instead of returning immediately.
Failure to terminate an owned process is reported rather than silently
ignored.
The process timeouts apply separately to each termination phase and
each process. They are not a single five-second deadline for the whole
shutdown operation, nor does this PR establish an end-to-end deadline
for every startup or shutdown action.
Reader shutdown continues to use the existing reader stop mechanism.
This change adds null-safe invocation and reference cleanup; it does
not introduce reader-thread joins or redesign reader synchronization.
The startup cleanup covers the launch, attach and program-start paths
described above. It is not a general recovery mechanism for arbitrary
failures in every test-specific setup hook.
No product code, test exclusions or existing test assertions are
changed.
How to test
Run
org.eclipse.debug.jdi.tests.AutomatedSuiteusing the repository'sexisting JDI test configuration. Both new test classes are registered
in that suite, which also performs the shared harness initialization.
The current change adds 20 regression tests.
VMStartupCleanupTest— 18 testsThe lifecycle tests inject failures during launch, attach and program
startup. They exercise the JUnit
runBare()path and verify thatcleanup occurs even though the fixture's normal
tearDown()is notcalled after failed setup.
Coverage includes cleanup before the event reader exists, reader stop
requests and reference clearing, preservation of the original startup
failure, automatic/custom port handling, repeated shutdown, and a
subsequent startup after successful cleanup.
Process test doubles cover normal and forced termination, bounded
waits, interruption before or during cleanup, missing/already exited
processes, continued cleanup after another process fails, and
diagnostics when a PID is unavailable.
A separate test starts a real Java subprocess, waits until it signals
readiness, injects an attach failure, and verifies that startup cleanup
terminates the subprocess. This supplements the deterministic
test-double coverage with an actual process-lifecycle check.
VMConnectionTimeoutTest— 2 testsThese tests use the real JDI connector and a local socket peer that
accepts the connection and reads the JDWP handshake without replying.
They verify that the connection attempt fails within the test's
bounded wait, retains the transport timeout and pre-cleanup diagnostics,
and reaches owned-process cleanup when invoked through the startup
entry point.
The socket peer is deliberately constructed to reproduce the stalled
handshake condition. These tests do not depend on reproducing an
intermittent CI failure.
CI status
For commit
bbf02c17a90b2a23563bde8d02be9574201343a0, GitHub reportscontinuous-integration/jenkins/pr-headas successful:Jenkins PR-992, build 6
This records the reported CI status for that revision. It does not
establish that all historical startup failures share one cause or that
every platform-specific process-termination behavior has been tested.
Relationship to #990
This PR does not depend on the breakpoint-removal implementation in
#990 and
contains no breakpoint-specific changes.
The intended integration order is to review and merge this
infrastructure change first, then update #990 to the resulting upstream
state. This keeps the shared test-harness behavior changes separately
reviewable and out of the final breakpoint-specific diff.
Author checklist