Skip to content

fix: parse ps output for PIDs narrower than the PID column - #406

Merged
gmegidish merged 1 commit into
mobile-next:mainfrom
bjjeong:fix/ps-pid-column-padding
Sep 11, 2026
Merged

gmegidish merged 1 commit into
mobile-next:mainfrom
bjjeong:fix/ps-pid-column-padding

Conversation

@bjjeong

@bjjeong bjjeong commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

listAllProcesses (devices/simulator.go) drops most of the process table.

ps right-aligns the PID column, so a PID with fewer digits than the column width is emitted with leading padding:

  PID COMMAND
    1 /sbin/launchd
  324 /usr/libexec/logd
 1637 /Users/.../devicekit-iosUITests-Runner ... DEVICEKIT_LISTEN_PORT=13180
10418 /usr/sbin/cfprefsd

The parser splits each line at the first space. For a padded line that index is 0, so line[:spaceIndex] is the empty string, strconv.Atoi fails, and the line is skipped. (The existing strings.TrimSpace doesn't help — it runs on an already-empty slice.)

The result is that only processes whose PID exactly fills the column are ever returned. On macOS the column is 5 wide, so only 5-digit PIDs survive. Measured on a developer machine:

ps reports  : 918 processes
parser found: 389 processes
DROPPED     : 529 (58%)
PID digit-lengths surviving the parser: map[5:389]

Impact

The affected consumer is findDeviceKitProcessForDevice, which returns agent process not found whenever the running agent's PID is narrower than the column. From there:

  1. StartAgent's getDeviceKitPort() probe fails, so a healthy, already-running agent looks absent.
  2. It falls through and allocates a new port via FindAvailablePortInRange.
  3. simctl launch on an already-running app returns the existing instance — the new DEVICEKIT_LISTEN_PORT is never applied, and the agent keeps listening on its original port.
  4. WaitForAgentWithDiagnostics polls the new port and times out.
  5. The error path runs _ = s.TerminateApp(agentBundleID) (devices/simulator.go:566), killing the working agent.

So the agent is torn down on essentially every call. We hit this as a CI flake: on a fresh runner the agent was verifiably alive (PID 1637, listening on 13180, health endpoint 200, visible to ps -E), yet the next mobilecli call reported agent process not found and killed it.

It reproduces deterministically as a function of PID width, which is why it looks environment-specific: long-lived hosts that have passed PID 10000 parse correctly by accident, while freshly booted CI machines still at low PIDs fail every time. Forcing the PID counter past 9999 on an affected runner — changing nothing else — made the agent get found and reused on every call.

Fix

Trim the leading padding before splitting off the PID field. The parsing is extracted into parsePsOutput so it can be tested without shelling out; listAllProcesses keeps its existing signature and behaviour.

The header line is still skipped — "PID COMMAND" fails Atoi — and the command field, which extractEnvValue reads DEVICEKIT_LISTEN_PORT back out of, is unchanged.

Tests

devices/simulator_process_test.go covers padded PIDs, header skipping, command/environment preservation, agent lookup by device path, and malformed lines. All five fail on main and pass with the fix:

--- FAIL: TestParsePsOutputIncludesPaddedPIDs
    Expected 4 processes, got 1
--- FAIL: TestParsePsOutputPreservesCommandAndEnvironment
    Expected to find the devicekit runner process
--- FAIL: TestParsePsOutputFindsAgentByDevicePath
    Expected to find the running agent process for the device
--- FAIL: TestParsePsOutputHandlesMalformedLines
    Expected 2 processes, got 0: []

Verified against the real process table: 918 of 918 processes found, up from 389.

go test ./... passes across the repo. gofmt and go vet are clean, and golangci-lint run ./devices/... reports the same 30 pre-existing issues in simulator.go before and after — no new ones, and none in the new test file.

Summary by CodeRabbit

  • Bug Fixes
    • Improved process detection when process IDs are padded with spaces.
    • Process listings now correctly skip headers and ignore malformed entries without dropping valid processes.
    • Preserved command and environment details in detected process information.
    • Improved agent lookup using device paths.

ps right-aligns the PID column, so any PID with fewer digits than the
column width is emitted with leading padding (" 1637 /path/to/binary").
listAllProcesses split each line at the first space, which for a padded
line is index 0, producing an empty PID field that fails to parse and
causes the line to be dropped. Only processes whose PID exactly fills
the column were ever returned -- on macOS, where the column is 5 wide,
that means only 5-digit PIDs.

Measured on a developer machine: ps reported 918 processes and
listAllProcesses returned 389, all of them 5-digit PIDs.

The practical effect is on findDeviceKitProcessForDevice, which fails
with "agent process not found" whenever the running agent's PID is
narrower than the column. StartAgent then treats the healthy agent as
absent, relaunches it on a newly allocated port, and simctl launch
returns the already-running instance still listening on its original
port. The readiness wait on the new port times out and the error path
terminates the working agent, so the agent is torn down on every call.

This is most visible on freshly booted CI machines, whose PIDs are
still low; hosts that have been up long enough to pass 10000 happen to
parse correctly.

Trim the leading padding before splitting off the PID field, and
extract the parsing into parsePsOutput so it can be tested without
shelling out.
@coderabbitai

coderabbitai Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: ce725b90-6171-43cf-909e-e5a4f99b4c8e

📥 Commits

Reviewing files that changed from the base of the PR and between 3312acd and e9c10cf.

📒 Files selected for processing (2)
  • devices/simulator.go
  • devices/simulator_process_test.go

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The process listing parser was extracted into parsePsOutput. It now handles right-aligned PID values, skips headers and malformed lines, and preserves command data. Tests cover parsing, environment extraction, agent lookup, and invalid input.

Changes

Process listing parser

Layer / File(s) Summary
Parser extraction and validation
devices/simulator.go, devices/simulator_process_test.go
parsePsOutput trims leading whitespace before parsing PIDs and skips the ps header and malformed lines. listAllProcesses delegates /bin/ps output to the helper. Tests cover padded PIDs, command and environment data, agent lookup, and invalid input.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Severity of issue fixed: Low

Suggested reviewers: gmegidish, hakanor

Merge Risk: ⚪ Minimal · up to e9c10

The parser now retains right-aligned PIDs while preserving process command and environment data. The change is ready to merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: fixing ps output parsing for PIDs that are narrower than the right-aligned PID column.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@gmegidish

Copy link
Copy Markdown
Member

@bjjeong excellent find! I'm amazed that I never ran into an issue, cause I haven't rebooted for over a month :). Will merge in a bit and credit in CHANGELOG. Thanks!!

@gmegidish
gmegidish merged commit 57e11a6 into mobile-next:main Sep 11, 2026
11 checks passed
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.

2 participants