Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Symbolicate iOS Crash Logs on a Remote Mac

Symbolication is CPU-light and only needs the Xcode command-line utilities, so Figment's $29 virtual Intel macOS plan (2 vCPU, 8 GiB RAM, 80 GiB disk; browser console access) is enough to run every command in this guide against an arm64 iOS crash log. symbolicatecrash and atos read dSYM bytes; they do not need to execute arm64 code. Pick the i3 Mac at $49/month if you also want to archive builds on the same host, or an M-series Mac from $95/month (exact chip and RAM confirmed in the quote) if your pipeline also runs Apple-silicon-only builds. Physical i3 and M-series plans collect requests until supply is confirmed. Start at /mac-hosting/start.

A crash from TestFlight arrives as a column of hex addresses like 0x000000010003c5a4. Those addresses are only readable once the matching dSYM is fed to Apple's tooling. The workflow is documented by Apple in Adding identifiable symbol names to a crash report.

Why Crash Logs Show Hex Addresses

When Xcode compiles a release build it strips debug symbols from the binary and writes them into a separate .dSYM bundle alongside the .xcarchive. The distributed app carries only load addresses and offsets, so a crash recorded by the OS is just program counters and a call stack.

Symbolication maps each address back to a source file and line. The one rule you cannot fudge is UUID matching: every build has a unique UUID baked into both the Mach-O binary and its dSYM. If the dSYM UUID does not match the UUID recorded in the crash log header, the output is either blank or wrong. Rerunning the tool will not fix a UUID mismatch.

Find the Right dSYM by UUID

The crash log's Binary Images: section lists each loaded image with its compact UUID in angle brackets:

Binary Images:
0x100000000 - 0x1003fffff  MyApp arm64  <A1B2C3D4E5F600112233445566778899> /var/containers/...

That 32-character hex string is the compact form. dwarfdump --uuid prints the same value with RFC 4122 dashes, so you strip dashes before comparing:

# UUID of a local dSYM
dwarfdump --uuid MyApp.dSYM/Contents/Resources/DWARF/MyApp
# UUID: A1B2C3D4-E5F6-0011-2233-445566778899 (arm64) MyApp

# Search the Archives folder, normalise to compact form
COMPACT_UUID="A1B2C3D4E5F600112233445566778899"
find ~/Library/Developer/Xcode/Archives -name "*.dSYM" \
  -exec dwarfdump --uuid {}/Contents/Resources/DWARF/* \; 2>/dev/null \
  | tr -d '-' \
  | grep -i "$COMPACT_UUID"

Substitute the compact UUID from your own crash log.

Run symbolicatecrash

symbolicatecrash ships inside Xcode's frameworks tree. Its path moves between Xcode versions, so locate it rather than hard-coding:

export DEVELOPER_DIR="$(xcode-select -p)"
SYMBOLICATECRASH=$(find "$DEVELOPER_DIR" -name symbolicatecrash -type f 2>/dev/null | head -1)

"$SYMBOLICATECRASH" MyApp.crash MyApp.dSYM > MyApp-symbolicated.crash
head -60 MyApp-symbolicated.crash

A symbolicated frame reads:

4   MyApp    0x000000010003c5a4  -[MyViewController viewDidLoad] + 112  (MyViewController.m:88)

If frames still show <redacted> or hex, the dSYM UUID does not match. Download the archive's dSYM from App Store Connect (see the final section) and try again.

A UUID-Driven Symbolication Helper

This script takes one .crash file, extracts the UUID of the first binary image, walks the Archives folder for a dSYM with the same UUID, and symbolicates. It exits non-zero on any failure, so a broken run cannot be mistaken for a success.

#!/usr/bin/env bash
# auto-symbolicate.sh — usage: ./auto-symbolicate.sh MyApp.crash
set -euo pipefail

CRASH_FILE="${1:?Usage: $0 path/to/file.crash}"

# 1. Extract the compact UUID of the first image in Binary Images.
UUID_COMPACT=$(grep -Eo '<[0-9A-Fa-f]{32}>' "$CRASH_FILE" | head -1 | tr -d '<>')
[[ -z "$UUID_COMPACT" ]] && { echo "No UUID in Binary Images section"; exit 1; }
echo "Target UUID: $UUID_COMPACT"

# 2. Find a matching dSYM in Archives.
DSYM=""
while IFS= read -r -d '' candidate; do
  if dwarfdump --uuid "$candidate"/Contents/Resources/DWARF/* 2>/dev/null \
       | tr -d '-' | grep -qi "$UUID_COMPACT"; then
    DSYM="$candidate"; break
  fi
done < <(find ~/Library/Developer/Xcode/Archives -name "*.dSYM" -print0 2>/dev/null)

if [[ -z "$DSYM" ]]; then
  echo "No matching dSYM; fetch it from App Store Connect for build UUID $UUID_COMPACT"
  exit 1
fi
echo "Using dSYM: $DSYM"

# 3. Symbolicate.
export DEVELOPER_DIR="$(xcode-select -p)"
SYMBOLICATECRASH=$(find "$DEVELOPER_DIR" -name symbolicatecrash -type f | head -1)
OUTPUT="${CRASH_FILE%.crash}-symbolicated.crash"
"$SYMBOLICATECRASH" "$CRASH_FILE" "$DSYM" > "$OUTPUT"
echo "Wrote $OUTPUT"

This is an example, not a benchmarked pipeline. Run it against your own .crash and dSYM and inspect the output before trusting it.

One Address at a Time With atos

When you only need the top frame of an exception, atos is quicker than running the full script:

# atos -arch ARCH -o DSYM_OR_BINARY -l LOAD_ADDRESS TARGET_ADDRESS
atos \
  -arch arm64 \
  -o MyApp.dSYM/Contents/Resources/DWARF/MyApp \
  -l 0x100000000 \
  0x000000010003c5a4

The load address (-l) is the first column of the matching Binary Images row. atos prints the function name and source line on stdout.

Let App Store Connect Symbolicate For You

If you upload dSYMs with a build, App Store Connect symbolicates crashes server-side and shows readable traces in Organizer. Apple documents the switch in Building your app to include debugging information: set uploadSymbols to true in your ExportOptions.plist, or tick "Upload your app's symbols" in Organizer when distributing. Xcode then zips the dSYMs and sends them as part of the upload. No separate API call is required for the standard path; the App Store Connect API is only needed if you re-upload symbols for an existing build.

A Quick Cost Worksheet

If you only use the remote Mac to triage crashes, hours of use per month is low and the entry price dominates:

  • Figment virtual Intel macOS: $29/month × 12 = $348/year.
  • CloudMac M4/16 GB/256 GB at $99.99/month (cloudmac.org/pricing) = $1,199.88/year, or $999/year prepaid (cloudmac.org) — $200.88 less than monthly, and still well above the $29 VM for symbolication-only work.
  • Figment M-series from $95/month × 12 = $1,140/year on the monthly entry price; the exact chip/RAM is quoted, so do not treat it as equal hardware to CloudMac's M4/16 GB.

If your crash triage also needs real arm64 execution (for example to reproduce a bug under lldb on arm64), step up to an M-series plan and confirm the exact configuration in the quote.

The dSYMs you symbolicate here are produced by whatever archives your CI pipeline runs; see the GitHub Actions self-hosted runner guide for keeping every archive and its dSYMs. If a crash points to a data race or a dangling object, the Thread Sanitizer guide and the Memory Graph Debugger guide are the next places to look.

Pick a plan at /mac-hosting or jump straight to /mac-hosting/start.


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms