Add proc_regex option to filter which processes open the TCP debug port#1178
Add proc_regex option to filter which processes open the TCP debug port#1178crystianleao wants to merge 1 commit intoruby:masterfrom
Conversation
So how about: |
|
optional thought: we can specify exact match with but it should be regexp (no exact match usecases)? |
|
Good catch, didn't see the ambiguity with Regarding exact match vs regexp: I built this to debug So exact matches would be brittle here, they'd break if That said, supporting both wouldn't hurt, |
…g port Introduces RUBY_DEBUG_OPEN_PROCTITLE / --open-proctitle=PROCTITLE. When set, UI_TcpServer#accept compares the value against $0 in each process. If it matches, the TCP listening port is opened normally; if it does not, accept() returns silently without opening a port (and without affecting the rest of the debugger session). The value can be either form: - "foo" => exact-match string (compared with ==) - "/foo/i" => regexp (parsed from /pattern/[flags]) Use case: forking job runners (e.g. solid_queue) where the supervisor spawns several differently-named child processes. The supervisor's startup order is non-deterministic, so --port-range from ruby#1119 cannot target a specific worker. With --open-proctitle='/\Asolid-queue-worker/', only processes whose $0 matches the regexp will open a port; the supervisor and non-matching workers run normally. To handle the timing window between fork and Process.setproctitle in the child, accept() waits up to 5 seconds for $0 to change from the value captured on the first accept call (tracked in InitialProcInfo) before evaluating the match. The regexp form is compiled once in initialize; an invalid pattern raises ArgumentError instead of being deferred to accept time. The README documents that the option is intended to be used with --nonstop (-n): without --nonstop, RUBY_DEBUG_OPEN arms an initial-suspend breakpoint that non-matching processes would still hit and block on. With --nonstop, no initial breakpoint is set and non-matching processes run unaffected. Tests cover parsing of both forms, initialize-time validation, the match path (regexp and string), and the no-match path with --nonstop (port not opened, program runs through).
Summary
Adds
--open-proctitle=PROCTITLE(env varRUBY_DEBUG_OPEN_PROCTITLE). When set,UI_TcpServer#acceptcompares the value against$0in each process and opens the listening TCP port only when it matches; otherwise it returns silently without affecting the rest of the debugger session.The value accepts two forms:
"foo"-> exact-match string (compared with==)"/foo/i"-> regexp, parsed from/pattern/[flags]Following discussion with @ko1, the single-flag dual-form design was preferred over a
-regexp-suffixed flag.Motivation
In multi-process job runners (the use case that drove this change is
solid_queue), the supervisor forks several differently-named child processes and renames them viaProcess.setproctitle. The proctitles insolid_queuelook like:./bin/jobs
solid-queue-worker(0.9.0): waiting for jobs in *
solid-queue-dispatcher(0.9.0): dispatching every 1 seconds
--port-range(#1119) addresses port collisions when several processes try to bind, but it cannot select which process should be debuggable. Startup order insolid_queueis non-deterministic, so a fixed port-in-range mapping is not viable.With
--open-proctitle='/\Asolid-queue-worker/', only worker processes whose$0matches will open a port; the supervisor and the dispatcher run unaffected. Exact-match form is convenient when proctitles are stable, e.g.--open-proctitle='my-worker'.Behaviour
UI_TcpServer.parse_open_proctitle. An invalid regexp raisesArgumentErrorat startup rather than being deferred to accept time.forkandProcess.setproctitlein the child,accept()waits up to 5 seconds for$0to change from the value captured on the first accept call (tracked inInitialProcInfo) before evaluating the match.accept()simply returns without yielding. The reader thread exits cleanly, the rest of the debugger session is left intact, and any forked children inherit a working session that re-evaluates the match in their own context.--nonstop(-n). Without--nonstop,RUBY_DEBUG_OPENarms an initial-suspend breakpoint that non-matching processes would hit and block on while waiting for a connection that will never arrive.Example
The supervisor (./bin/jobs) does not match; no port is opened. Each worker, after fork and setproctitle, matches and opens the port.
New tests in test/console/open_proctitle_test.rb: