Debugging and Instruments
Diagnose crashes, memory leaks, hangs, and performance bottlenecks using LLDB, Xcode Memory Graph Debugger, unified logging/signposts, and Instruments.
Scope Boundary: Route command-line
.memgraphanalysis toios-memgraph-analysisand ETTrace tracing toios-ettrace-performance.
Contents
- LLDB Debugging Workflow
- Memory Graph & Retain Cycles
- Hangs & Signpost Diagnostics
- Instruments Profiling
- Common Mistakes
- Review Checklist
- References
LLDB Debugging Workflow
Follow a structured, non-invasive triage loop:
- Reproduce in Debug configuration; pause at the narrowest relevant line.
- Inspect values without executing code using
v, falling back topoonly for formatted descriptions. - Traverse frames and threads to isolate invalid states.
- Set conditional breakpoints or watchpoints to catch unintended state transitions.
(lldb) br set -f OrderViewModel.swift -l 42 # Break at file and line
(lldb) v orderState # Inspect variable without code execution
(lldb) po orderState # Print debug description
(lldb) bt all # Print backtrace for all threads
(lldb) frame select 2 # Switch execution frame
(lldb) br modify 1 -c "orderId == 404" # Set condition on breakpoint
(lldb) w set v self.totalAmount # Watchpoint on memory mutation
Memory Graph & Retain Cycles
Enable Malloc Stack Logging (Edit Scheme > Run > Diagnostics) to record allocation backtraces.
- Navigate to the suspect view, perform user actions, and pop/dismiss the view.
- Click Debug Memory Graph in the Xcode debug bar.
- Look for purple exclamation mark warnings indicating leaked memory or retain cycles.
- Select the instance node to inspect the strong reference graph.
Common fix: break closure cycles using [weak self] or assign delegates weakly (weak var delegate: SomeDelegate?).
Hangs & Signpost Diagnostics
Diagnose main thread stalls and measure execution intervals using unified logging and OSSignposter:
import os
let logger = Logger(subsystem: "com.example.app", category: "DataSync")
let signposter = OSSignposter(logger: logger)
func loadRecords() async throws {
let signpostID = signposter.makeSignpostID()
let state = signposter.beginInterval("LoadRecords", id: signpostID)
defer { signposter.endInterval("LoadRecords", state) }
// Intensive operation
}
View intervals and subsystem events in Instruments under the os_signpost instrument.
Instruments Profiling
Always profile in Release configuration outside the debugger (Product > Profile):
- Time Profiler: Identifies hot execution paths and CPU usage. Expand invert-call-tree and hide-system-libraries to focus on app code.
- Allocations & Leaks: Tracks heap growth, memory allocations, and leaked memory over time.
- Core ML: Measures model execution latency, neural engine offload, and memory consumption.
- Energy Log / Network: Audits battery impact and network transfer efficiency.
Common Mistakes
- Using
pofor simple inspection:poevaluates expressions via the Swift runtime and can cause unexpected side effects or deadlocks. Usevto read values safely. - Profiling in Debug configuration: Debug builds disable compiler optimizations and produce distorted performance timings.
- Profiling with debugger attached: LLDB hooks introduce significant execution overhead. Use Instruments standalone.
- Ignoring purple warnings in Memory Graph: Purple warnings flag definite object leaks; resolve them before optimizing memory elsewhere.
- Blocking the main thread with synchronous work: Synchronous file I/O or network calls on
@MainActortrigger main-thread hangs.
Review Checklist
- LLDB inspection favors
voverpoto avoid side effects - Malloc Stack Logging enabled when capturing memory graphs
- Strong reference cycles broken with
[weak self]orweakdelegates - Profiling performed in Release build without debugger attachment
- Signposts implemented around performance-critical workflows
- Time Profiler call trees inspected with system libraries hidden
References
- LLDB command reference: references/lldb-patterns.md
- Instruments template guide: references/instruments-guide.md
- Logging (unified logging system)
- Logger
- OSSignposter
- Generating log messages from your code
- Recording performance data (signposts)
- Diagnosing memory, thread, and crash issues early
- Data races
- Reducing your app's memory use
- Profiling apps using Instruments
- Improving app responsiveness
- Analyzing your app's battery use
- Analyzing the performance of your shipping app