Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
# APP-4104: Tech Spec
## Problem
APP-4104 covers the macOS memory growth on the breadcrumb-forwarding path between Rust Sentry and Cocoa Sentry. The hot path is installed in `before_breadcrumb` and forwards every Rust `sentry::Breadcrumb` into the native bridge. Today that bridge creates `NSString` values with `alloc/init_str` and passes them across FFI without balancing ownership. In breadcrumb-heavy sessions, temporary bridge allocations can live far longer than intended and drive sustained process memory growth.
## Relevant code
- `app/src/crash_reporting/mod.rs (476-489)` — installs the `before_breadcrumb` callback and forwards macOS breadcrumbs through `mac::forward_breadcrumb`
- `app/src/crash_reporting/mac.rs (20-84)` — Rust-to-ObjC Cocoa Sentry bridge, including `forward_breadcrumb`, `set_user_id`, `set_tag`, and `to_nsstring`
- `app/src/platform/mac/objc/crash_reporting.m (67-81)` — native `recordBreadcrumb` implementation that constructs `SentryBreadcrumb`
- `crates/warpui/src/platform/mac/mod.rs (29-34)``make_nsstring`, the existing helper that returns an autoreleased `NSString`
- `app/src/app_services/mac.rs (20-29)` — another app-side example of returning an autoreleased `NSString`
## Current state
### Rust bridge
`init_sentry` registers `before_breadcrumb`, and the macOS branch calls `mac::forward_breadcrumb(&crumb)` before returning the original Rust breadcrumb unchanged. `forward_breadcrumb` extracts `message`, `category`, `level`, and `timestamp`, then calls `recordBreadcrumb`.
The string bridge in `app/src/crash_reporting/mac.rs` is currently:
- `unsafe fn to_nsstring(val: &str) -> id`
- implemented with `NSString::alloc(nil).init_str(val)`
- used by `init_cocoa_sentry`, `set_user_id`, `forward_breadcrumb`, and `set_tag`
That helper returns a retained Objective-C object. The crash-reporting bridge does not currently release or autorelease those values before returning to the caller.
### Objective-C bridge
`recordBreadcrumb` creates a `SentryBreadcrumb`, maps the level string via `levelFromString`, sets `category`, `message`, and `timestamp`, and then calls `[SentrySDK addBreadcrumb:crumb]`.
That file is compiled without ARC, so the native breadcrumb object itself must still be balanced explicitly after the `SentrySDK` call returns.
### Existing repo pattern
Warp already has established patterns for temporary Cocoa strings:
- `warpui::platform::mac::make_nsstring` returns an autoreleased `NSString`
- app-owned macOS bridges already use direct `.autorelease()` in places like `app_services/mac.rs`
The crash-reporting bridge is the outlier.
## Proposed changes
### 1. Replace retained bridge strings with autoreleased strings
Remove the retained `to_nsstring` pattern from `app/src/crash_reporting/mac.rs` and switch the file to the same autoreleased `NSString` strategy already used elsewhere in the repo.
Concretely:
- import `warpui::platform::mac::make_nsstring` at the top of the file
- use that helper for every bridge string created in this module
- apply the change consistently to `init_cocoa_sentry`, `set_user_id`, `forward_breadcrumb`, and `set_tag`
Using one helper for the whole file keeps the ownership model uniform at the Rust→ObjC boundary and fixes the hot breadcrumb path without leaving the same bug in the lower-volume tag and user paths.
### 2. Add a local autorelease boundary in `forward_breadcrumb`
Create an `NSAutoreleasePool` inside `app/src/crash_reporting/mac.rs::forward_breadcrumb` so the pool is active before any bridge `NSString` calls `autorelease()`, and drain it immediately after `recordBreadcrumb` returns.
This bounds the lifetime of the Rust-created bridge strings because they are now autoreleased while the per-breadcrumb pool is current, rather than relying on an unknown outer pool on whatever thread emitted the log record.
### 3. Keep native breadcrumb ownership explicit
Keep `recordBreadcrumb`'s existing `![SentrySDK isEnabled]` guard and field mapping logic, but explicitly balance the non-ARC `SentryBreadcrumb` allocation with `[crumb release]` after `[SentrySDK addBreadcrumb:crumb]`.
### 4. Preserve breadcrumb semantics
This fix is ownership-only. It does not change:
- which Rust log records become breadcrumbs
- how `before_breadcrumb` is registered
- the `message`, `category`, `level`, or `timestamp` values forwarded to Cocoa Sentry
- Sentrys native breadcrumb storage limits or filtering behavior
## End-to-end flow
1. Rust logging emits a record and Sentry Rust turns it into a `sentry::Breadcrumb`.
2. `before_breadcrumb` forwards that breadcrumb to `mac::forward_breadcrumb`.
3. `forward_breadcrumb` creates a short-lived `NSAutoreleasePool`, then creates autoreleased `NSString` values for `message`, `category`, and `level`, and calls `recordBreadcrumb`.
4. `recordBreadcrumb` creates a native `SentryBreadcrumb`, sets its fields, passes it to `SentrySDK`, and explicitly releases its local ownership before returning.
5. `forward_breadcrumb` drains its local pool once the native call completes; only the objects retained by Cocoa Sentry remain live.
## Risks and mitigations
1. **Autoreleased arguments must be retained by the callee**
`SentryBreadcrumb` and `SentrySDK` must retain or copy any values they keep after `recordBreadcrumb` returns. This is the standard Objective-C ownership contract for stored properties, and the objects remain valid for the full duration of the call.
2. **Per-breadcrumb autorelease-pool overhead**
Adding a small pool around each breadcrumb has a fixed cost. That cost is negligible compared with the existing cross-language allocation work, and bounding memory is the higher-priority outcome for this path.
3. **Other macOS FFI bridges may still have separate ownership issues**
APP-4104 is scoped to crash reporting because that is the hot path implicated by the memory profile. A wider audit can be handled separately once this fix lands.
## Testing and validation
1. Build the macOS client path with Cocoa Sentry enabled to catch compile and linkage issues in the Rust and Objective-C bridge.
2. Run a breadcrumb-heavy macOS session and verify that RSS no longer grows linearly while breadcrumb forwarding remains active.
3. Capture a Sentry event after the change and confirm breadcrumb `message`, `category`, `level`, and `timestamp` still appear as before.
4. Confirm `set_user_id` and `set_tag` still reach Cocoa Sentry after they switch to the same autoreleased string helper.
## Follow-ups
- If APP-4104 resolves the memory growth, do a focused audit of other app-owned macOS FFI helpers that still call `NSString::alloc(nil).init_str(...)` directly so we can standardize on one safe bridge helper.