Skip to content

Polish iPad crash lines and log a failed USB connection once - #133

Merged
whoisaldo merged 3 commits into
release/0.3.4-candidatefrom
fix/ipad-crash-log-polish
Oct 30, 2026
Merged

whoisaldo merged 3 commits into
release/0.3.4-candidatefrom
fix/ipad-crash-log-polish

Conversation

@whoisaldo

Copy link
Copy Markdown
Member

What changed

Three small fixes to iPad support-log lines, one commit each.

Each MetricKit frame shows its address (Core/CrashReporting.swift)

Early MetricKit payloads can put the binary's load address in offsetIntoBinaryTextSegment, and then the offset alone can't place the frame. Each frame in frames= is now binary+0x<offset>@0x<address>, using the frame's address from the call stack tree (0 when MetricKit leaves it out, as it does for a missing offset).

The final scrub keeps the address: the line formats it from a UInt64, so it is at most 16 hex digits. The exemption is a lookbehind for +0x<offset>@, which only the frame list produces, because binary names are tokens without @. The same hex in a binary name is still <id>. Eight frames with 64-character names and maximum offsets and addresses stay under 2 KiB.

The termination code survives the scrub

64-bit codes such as EXC_GUARD and JETSAM reasons have 16 hex digits with a letter. The scrub turned them into code:<id>, so the reason the app was killed was lost. The hex scrub now skips the value after termination=namespace:<NAMESPACE> code: and nowhere else. termination() has already reduced that value to 0x plus at most 16 hex digits, or at most 20 decimal digits. The line has no code= field (exception_code= is a decimal Int64, which the scrub never matched).

Before and after, with synthetic data:

before: … frames=libsystem_kernel.dylib+0x1a2b00,EternalMonitor+0x104a2c000,EternalMonitor+0x1a2b20 termination=namespace:GUARD code:<id>
after:  … frames=libsystem_kernel.dylib+0x1a2b00@0x1e8f3ab00,EternalMonitor+0x104a2c000@0x104a3d2b4,EternalMonitor+0x1a2b20@0x104bcf2c8 termination=namespace:GUARD code:0xc00000000000ab12

The rest of the line (MetricKit crash app=0.3.4 build=370 os=ipados:26.1 os_build=23B85 at=2026-10-26T07:33:20Z exception_type=12(EXC_GUARD) exception_code=0 signal=9(SIGKILL) objc_exception=- thread=attributed) is unchanged.

A failed USB connection is logged once (Network/FramedLink.swift)

When the connection reported .failed, FramedLink logged NWConnection failed: …. fail() then logged the same failure again: through the connection that took the link (state=… USB connection failed: …), or as the link's own line while it waited for its preamble. The send path did the same with NWConnection send: …. #126 removed this extra line from the receive path; the other two now log only through fail(). The removed lines added nothing, because localizedDescription on an NWError already carries the errno and its text ("… (Network.NWError error 54 - Connection reset by peer)").

In practice the .failed handler rarely ran: with a receive outstanding, a reset reaches the receive callback first, and its fail() clears the state handler. FramedLink now takes its support-log writer as an init parameter (default SupportDiagnostics.shared), and the state switch moved into handle(_:), so a test can drive it.

Tests

  • CrashReportingTests: existing expectations now include each frame's @0x<address>, and a frame without one shows @0x0.
  • testEachFrameShowsItsAddress: a 16-hex-digit address is kept while the same digits in a binary name become <id>. Eight maximum-length frames stay under 2 KiB.
  • testALongTerminationCodeIsKept: a 16-hex-digit GUARD code is kept in termination=, and the same hex as a binary name is still scrubbed. A bare code:<hex> outside the termination field is still scrubbed, and a JETSAM code survives.
  • FramedLinkTests.testAFailedConnectionIsLoggedOnce: a .failed state gives one error [usb] USB connection failed: … line while no connection owns the link. With an owner it gives no line and one onError, and onClosed fires once either way.
  • scripts/test_ios.sh -only-testing:EternalMonitorTests on an iPad Pro 11-inch (M4) simulator: 349 tests, 0 failures on iOS 26.3 / Xcode 26.3, and 349 tests, 0 failures on iOS 18.6 / Xcode 16.4.
  • The new crash lines don't match the session_summary contract regex (they are crash events, not summaries), SupportDiagnostics.redact leaves them unchanged, and they still start MetricKit crash , which the daily check matches.

Release notes

iPad crash reports now include more detail to help us find the cause, and a failed USB connection is no longer logged twice.

Early MetricKit payloads can hold the binary's load address in the
text-segment offset, so the offset alone can't place the frame. Each
frame is now binary+0x<offset>@0x<address>, and the final scrub keeps
the address, which the line formats from a number. The same hex in a
binary name is still scrubbed.
64-bit codes such as EXC_GUARD and JETSAM reasons have 16 hex digits
with a letter, so the final scrub turned them into code:<id> and the
reason the app was killed was lost. The scrub now keeps the code value
in the termination field, which is already reduced to namespace and
code, and nowhere else.
A .failed connection logged "NWConnection failed" and then fail()
logged the same failure again, through the connection that took the
link or as the link's own line. A send failure did the same with
"NWConnection send". The receive path lost its extra line in #126; the
other two now log only through fail(). FramedLink takes its support-log
writer as an init parameter so a test can count the lines.
@whoisaldo
whoisaldo requested a review from Alitleis123 October 29, 2026 23:25
@whoisaldo whoisaldo added the 0.3.4 Part of the 0.3.4 release label Oct 29, 2026
@whoisaldo
whoisaldo marked this pull request as ready for review October 30, 2026 01:37
@whoisaldo
whoisaldo merged commit 3dc6b1e into release/0.3.4-candidate Oct 30, 2026
7 checks passed
@whoisaldo whoisaldo mentioned this pull request Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0.3.4 Part of the 0.3.4 release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants