From d762fe55a2d92b17a1cdeefdc60692a3fe78278c Mon Sep 17 00:00:00 2001 From: jwhur <57657645+JaewonHur@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:22:33 -0800 Subject: [PATCH] Launch a service with waitForDebugger if specified (#1101) This PR enables launching a service with `waitForDebugger` flag if the service label matches a given env variable `CONTAINER_DEBUG`. --- BUILDING.md | 36 +++++++++++++++++++++++ Sources/ContainerPlugin/LaunchPlist.swift | 19 +++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index 145735b1..a661460d 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -119,6 +119,42 @@ To revert to using the Containerization dependency from your `Package.swift`: bin/container system start ``` +## Debug XPC Helpers + +Attach debugger to the XPC helpers using their launchd service labels: + +1. Find launchd service labels: + + ```console + % container system start + % container run -d --name test debian:bookworm sleep infinity + test + % launchd list | grep container + 27068 0 com.apple.container.container-network-vmnet.default + 27072 0 com.apple.container.container-core-images + 26980 0 com.apple.container.apiserver + 27331 0 com.apple.container.container-runtime-linux.test + ``` + +2. Stop container and start again after setting the environment variable `CONTAINER_DEBUG_LAUNCHD_LABEL` to the label of service to attach debugger. Services whose label starts with the `CONTAINER_DEBUG_LAUNCHD_LABEL` will wait the debugger: + + ```console + % export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux.test + % container system start # Only the service `com.apple.container.container-runtime-linux.test` waits debugger + ``` + + ```console + % export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux + % container system start # Every service starting with `com.apple.container.container-runtime-linux` waits debugger + ``` + +3. Run the command to launch the service, and attach debugger: + + ```console + % container run -it --name test debian:bookworm + таз [6/6] Starting container [0s] # It hangs as the service is waiting for debugger + ``` + ## Pre-commit hook Run `make pre-commit` to install a pre-commit hook that ensures that your changes have correct formatting and license headers when you run `git commit`. diff --git a/Sources/ContainerPlugin/LaunchPlist.swift b/Sources/ContainerPlugin/LaunchPlist.swift index ed996b46..ff2297e6 100644 --- a/Sources/ContainerPlugin/LaunchPlist.swift +++ b/Sources/ContainerPlugin/LaunchPlist.swift @@ -18,6 +18,8 @@ import Foundation public struct LaunchPlist: Encodable { + static let debugTarget = "CONTAINER_DEBUG_LAUNCHD_LABEL" + public enum Domain: String, Codable { case Aqua case Background @@ -61,6 +63,21 @@ public struct LaunchPlist: Encodable { case waitForDebugger = "WaitForDebugger" } + static private func getWaitForDebugger(label: String, fromArg: Bool?) -> Bool? { + if let fromArg { + return fromArg + } + + let env = ProcessInfo.processInfo.environment + if let debugTarget = env[Self.debugTarget], + label == debugTarget || label.starts(with: debugTarget + ".") + { + return true + } + + return nil + } + public init( label: String, arguments: [String], @@ -93,7 +110,7 @@ public struct LaunchPlist: Encodable { self.disabled = disabled self.program = program self.keepAlive = keepAlive - self.waitForDebugger = waitForDebugger + self.waitForDebugger = Self.getWaitForDebugger(label: label, fromArg: waitForDebugger) if let services = machServices { var machServices: [String: Bool] = [:] for service in services {