Skip to content

[Bug]: Memory Leak while app is idle - StderrCapture produces runaway block allocations #433

Description

@Jolg42

Summary

My app crashed after a few minutes due to a memory leak, while open and idle on my iPad and laptop

Steps to reproduce

I do not know how to reproduce this yet in an isolated way.
Disabling .crashManager and .console during DebugSwift's setup fixed this for me.

Here is the analysis from Codex LLM, Sol, that I reviewed.

Expected behavior

Idle memory should stabilize, and stderr capture should not accumulate an unbounded number of pending dispatch blocks.

Actual behavior

After opening the app and then leaving it idle, memory continued growing
without any user interaction.

A 111-second Instruments capture reported:

  • 126.26 MiB persistent heap and anonymous VM
  • 2,704,061 persistent allocations
  • 890,181 persistent 64-byte allocations (54.33 MiB)
  • 892,073 persistent 48-byte allocations (40.84 MiB)
  • 890,084 persistent unknown objects (27.20 MiB)

Representative 48-byte allocations appeared only microseconds apart and had
the following allocation stack:

closure #1 in StderrCapture.startCapturingInternal()
closure #1 in closure #1 in StderrCapture.startCapturingInternal()
_Block_copy
_malloc_type_malloc_outlined

No app interaction intervals occurred during the relevant idle period.

The debug session later ended with signal 9.

A separate Mac Catalyst Debug run also grew to 139.45 GB while idle, although
that process was not captured with an allocation trace, so its cause has not
been independently confirmed.

Affected version

rc-1.18.16

Platform

iPadOS 27 beta 4

Logs or stack trace

### DebugSwift version

rc-1.18.16
Commit: b66e92c625c8308a91082b9b53cc5af6d4035db7

### Environment

- Physical iPad Pro 11-inch (3rd generation)
- iPadOS 27 beta
- Instruments 27
- Debug build
- DebugSwift initialized with:
  `.setup(disable: [.leaksDetector, .network, .webSocket])`

### Potential implementation concerns

StderrCapture's readability handler schedules work onto captureQueue before
`availableData` is consumed. Could repeated readability callbacks enqueue work
faster than captureQueue drains it?

StderrCapture also stores the numeric stderr file descriptor and later replaces
that descriptor with dup2. The stored value does not appear to be a duplicated,
separately owned reference to the original stderr destination.

These are hypotheses based on source inspection; the trace directly confirms
only that repeated retained allocations originate in
StderrCapture.startCapturingInternal().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions