Skip to content

Phase 2.5: Deferred activation — connect before enabling EXT_STMT#6

Merged
pronskiy merged 1 commit intophase2-observer-migrationfrom
phase2.5-deferred-activation
Mar 10, 2026
Merged

Phase 2.5: Deferred activation — connect before enabling EXT_STMT#6
pronskiy merged 1 commit intophase2-observer-migrationfrom
phase2.5-deferred-activation

Conversation

@pronskiy
Copy link
Copy Markdown
Collaborator

Summary

At RINIT, when a debug trigger is present (e.g. XDEBUG_SESSION=1), attempt TCP connect to the client before enabling ZEND_COMPILE_EXTENDED_STMT. If no client is listening (ECONNREFUSED), keep observer_active=0 and skip EXT_STMT — the extension stays dormant.

This eliminates ~2.3× overhead in the triggered-no-client scenario.

Approach: Two-stage connection

  1. RINIT: TCP connect only via xdebug_early_connect_to_client() — no DBGp handshake (needs program_name from first op_array)
  2. First function call: xdebug_init_debugger() sees socket already open → skips connect → does DBGp handshake

Also fixes RINIT trigger check to respect start_with_request=no and XDEBUG_IGNORE, matching xdebug_debug_init_if_requested_at_startup() behavior.

Files changed

  • xdebug.c — RINIT early connect + EXT_STMT decision
  • src/debugger/com.cxdebug_early_connect_to_client(), modified xdebug_init_debugger() to reuse pre-opened socket
  • src/debugger/com.h — declaration
  • src/debugger/debugger.c — initialize socket to -1

Benchmarks (build server, PHP 8.6.0-dev)

Configuration Before After
Vanilla (no ext) ~0.55s ~0.55s
Loaded, no trigger ~0.56s ~0.55s
Loaded, triggered, no client ~1.63s (2.3×) ~0.55s (~0%)

Tests

54 failures — identical to Phase 2 baseline. Zero regressions.

At RINIT, when a debug trigger is present, attempt TCP connect to the
client before enabling ZEND_COMPILE_EXTENDED_STMT. If no client is
listening (ECONNREFUSED), keep observer_active=0 and skip EXT_STMT.

This eliminates ~2.3x overhead in the triggered-no-client scenario
(e.g. XDEBUG_SESSION=1 but no IDE listening). The extension now stays
dormant, matching the untriggered performance (~10% observer floor).

Two-stage connection approach:
1. RINIT: TCP connect only (xdebug_early_connect_to_client)
2. First function call: DBGp handshake on the pre-opened socket

Also fixes RINIT trigger check to respect start_with_request=no
and XDEBUG_IGNORE, matching xdebug_debug_init_if_requested_at_startup
behavior.

Benchmark (build server, PHP 8.6.0-dev):
- Vanilla: ~0.55s
- Loaded, no trigger: ~0.56s (~0%)
- Loaded, triggered, no client (before): ~1.63s (~2.3x)
- Loaded, triggered, no client (after): ~0.55s (~0%)

Test results: 54 fail (same as Phase 2 baseline, zero regressions).
@pronskiy pronskiy merged commit 296bf59 into phase2-observer-migration Mar 10, 2026
2 of 8 checks passed
pronskiy added a commit that referenced this pull request Mar 11, 2026
Phase 2.5: Deferred activation — connect before enabling EXT_STMT
@pronskiy pronskiy deleted the phase2.5-deferred-activation branch March 24, 2026 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant