Skip to content

diag(macos): os_signpost markers around transition + fixedSize boundaries (LUM-1116 Phase 1)#27552

Merged
ashleeradka merged 1 commit into
mainfrom
do/os-signpost-transition-markers
Apr 22, 2026
Merged

diag(macos): os_signpost markers around transition + fixedSize boundaries (LUM-1116 Phase 1)#27552
ashleeradka merged 1 commit into
mainfrom
do/os-signpost-transition-markers

Conversation

@ashleeradka
Copy link
Copy Markdown
Contributor

@ashleeradka ashleeradka commented Apr 22, 2026

Summary

  • Add layoutHangSignpost(_:) SwiftUI view modifier in Telemetry/PerfSignposts.swift that emits os_signpost(.event …) markers on onAppear / onDisappear against a new layout-hangs log category.
  • Apply it at every .transition(.move…) site in the macOS app (13 sites) and at two .fixedSize() component boundaries (composer outer VStack, queued-messages drawer).
  • Pure diagnostics — no behavior change, no new runtime cost beyond the os_signpost syscall. StaticString names guarantee zero allocation on the main thread.

Why

Sentry main-thread hangs in MACOS-66 and related (LUM-1116) fingerprint SizeFittingLayoutComputer.Engine.explicitAlignment → _HStackLayout × 2 → Color → MoveTransition.MoveLayout → _VStackLayout × 5 → StackLayout.prioritize → MutableCollection.sort. The stack contains zero user-defined view names, so we can't tell which .transition(.move…) call site is driving it. LUM-1111 (MACOS-KE) separately confirms a sidebar transition can hang — direct evidence that this code path is implicated.

With .event signposts emitting a view name on appear/disappear, the next hang event will ship with a recent signpost breadcrumb naming the active view — the missing piece needed to cut a Phase 2 targeted fix.

This PR does not fix any hang by itself.

Instrumented sites

.transition(.move…) sites

File Line Signpost name
Features/Chat/ComposerSection.swift 94 composer.watchProgress
Features/Chat/ChatView.swift 214 chat.searchBar
Features/Chat/ChatView.swift 471 chat.queuedMessagesDrawer
Features/Chat/ChatView.swift 659 chat.btwOverlay
Features/Chat/ChatErrorToastView.swift 135 chat.errorToast
Features/Chat/ChatErrorToastView.swift 239 chat.creditsExhaustedBanner
Features/Chat/ChatErrorToastView.swift 274 chat.compactionCircuitOpenBanner
Features/Chat/ChatErrorToastView.swift 327 chat.missingApiKeyBanner
Features/Chat/RecoveryModeBanner.swift 85 chat.recoveryModeBanner
Features/Chat/ComposerView.swift 144 composer.slashCommandPopup
Features/Chat/ComposerView.swift 153 composer.emojiPickerPopup
Features/Chat/MessageListHelperViews.swift 43 chat.scrollToLatestOverlay
Features/MainWindow/PanelCoordinator.swift 917 panel.messageInspector
Features/MainWindow/MainWindowZoomIndicator.swift 17 mainWindow.zoomIndicator
Features/MainWindow/MainWindowToastOverlay.swift 22 mainWindow.toastOverlay
Features/MainWindow/Panels/MemoriesPanel.swift 121 panel.memories.detailSheet
Features/MainWindow/Panels/IdentityPanel.swift 130 panel.identity.sidebar

.fixedSize() component boundaries

File Line Signpost name
Features/Chat/ComposerView.swift 172 composer.outerVStack.fixedSize
Features/Chat/QueuedMessagesDrawer.swift 46 chat.queuedMessagesDrawer.fixedSize

Note: leaf Text.fixedSize() calls are intentionally NOT instrumented — they're cheap, frequent, and not the layout-engine boundary that matters for the SizeFittingLayoutComputer stack. LazyVStack cells are also intentionally skipped to avoid O(visible) × frames noise.

Implementation

Extended the existing PerfSignposts namespace (Telemetry/PerfSignposts.swift) instead of adding a new helper — there was already an os_signpost wrapper used for profiling, so we reuse the same module:

static let layoutHangs = OSLog(
    subsystem: Bundle.appBundleIdentifier,
    category: "layout-hangs"
)

extension View {
    func layoutHangSignpost(_ name: StaticString) -> some View {
        self
            .onAppear  { os_signpost(.event, log: PerfSignposts.layoutHangs, name: name, "appear") }
            .onDisappear { os_signpost(.event, log: PerfSignposts.layoutHangs, name: name, "disappear") }
    }
}

StaticString is required (not String) so the call site can't allocate. The log is a dedicated category (layout-hangs) distinct from the Points-of-Interest log that PerfSignposts already uses, so these can be filtered cleanly without drowning in Markdown-parse / scroll-intent / body-evaluation signposts.

Test plan

  • swift build --target VellumAssistantLib compiles cleanly in the worktree.
  • After merge, cut a build and open Instruments → Points of Interest. In the filter bar, scope to category layout-hangs. Trigger any instrumented view (send a message to queue it, open a panel, show a banner) and confirm appear / disappear events land in the track with the expected name.
  • Ship on next release. Wait for MACOS-66 (or a related SizeFittingLayoutComputer.Engine.explicitAlignment hang) to fire. Pull the spindump from the attached event, find the last few os_signpost entries on the layout-hangs category, and identify the view name that was appearing/disappearing when the hang started. Use that to cut a targeted Phase 2 fix.

Reading the signposts in Instruments

1. Product → Profile (or `instruments -t "Points of Interest" <app>`)
2. Switch to the "Points of Interest" instrument.
3. In the detail panel, expand "Subsystems" and scope to
   `com.vellum.vellum-assistant` / category `layout-hangs`.
4. Each event shows the view name as its message string ("appear" or
   "disappear"). The track visualizes which view is on-screen when.

In a Sentry hang spindump, search for layout-hangs in the unified-log section for the equivalent breadcrumb.

Constraints respected

  • No behavior change — only .onAppear / .onDisappear callbacks added via a non-intrusive some View modifier.
  • .event markers only (no begin/end intervals, no OSSignpostID tracking).
  • No instrumentation inside LazyVStack cells (O(visible × frames) blast radius).
  • StaticString names — no main-thread allocation risk.
  • Zero-cost in release builds (signposts elide to a syscall when the log is not being streamed).

Linear / Sentry

  • Linear: LUM-1116 — Phase 1 (instrumentation).
  • Sentry: MACOS-66 and related SizeFittingLayoutComputer.Engine.explicitAlignment main-thread hangs.
  • Related: LUM-1111 (MACOS-KE, SidebarSectionView.body.getter:147).

Deferred

Phase 2 will use the signpost breadcrumb from the next production hang to cut a targeted fix (reshape the transition/fixedSize combination on the culprit view). No Phase 2 work in this PR.


Open in Devin Review

…ries (LUM-1116 Phase 1)

Add `layoutHangSignpost(_:)` view modifier that emits `.event` os_signpost
markers (category `layout-hangs`) on appear/disappear, and apply it at
every `.transition(.move…)` site plus the two confirmed `.fixedSize()`
component boundaries.

Production Sentry main-thread hangs (MACOS-66 and related) land with a
fingerprint of `SizeFittingLayoutComputer.Engine.explicitAlignment →
_HStackLayout → Color → MoveTransition.MoveLayout → nested _VStackLayouts
→ StackLayout.prioritize → MutableCollection.sort`, but no user-defined
view names — so we can't tell which `.transition(.move…)` call site is
the culprit. These signposts ship in the spindump / Instruments track
alongside future hang events, turning the ambiguous stack into an
actionable view identifier.

Pure diagnostics — no behavior change. `.event` signposts with
StaticString names allocate nothing and are safe on the main thread.
@linear
Copy link
Copy Markdown

linear Bot commented Apr 22, 2026

@ashleeradka ashleeradka merged commit 12b4520 into main Apr 22, 2026
6 checks passed
@ashleeradka ashleeradka deleted the do/os-signpost-transition-markers branch April 22, 2026 20:31
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

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.

1 participant