dSYM 업로드

빌드의 dSYM을 올려 크래시와 에러 스택 트레이스를 함수 이름, 파일, 줄 번호로 복원하고 Xcode처럼 스레드별로 확인합니다.

릴리스 빌드의 크래시는 주소만 남깁니다. 그 빌드의 dSYM을 올려 두면 Sophonz가 주소를 GalleryView.body.getter (GalleryView.swift:42)처럼 함수와 소스 위치로 바꿔 보여 줍니다. dSYM은 앱의 앱 버전 아래에 보관되고, 스택 트레이스의 바이너리 이미지 UUID로 매칭됩니다.

복원되는 대상

데이터속성내용
크래시spz.payload (KSCrash 리포트)모든 스레드, 크래시 원인(EXC_BREAKPOINT / SIGTRAP 등), 레지스터, 바이너리 이미지
에러 로그spz.stacktrace.ios로그를 남긴 스레드의 프레임

프레임마다 이미지 이름, 이미지 UUID, 로드 주소, 명령 주소가 함께 전송되므로, 앱과 앱에 포함된 프레임워크 각각을 자기 dSYM으로 복원합니다.

Apple 시스템 프레임워크(UIKit, SwiftUICore, libswiftCore.dylib 등)는 dSYM을 올릴 필요가 없습니다. 기기가 기록한 심볼을 Swift 디맹글링해서 Array.subscript.getter 같은 읽을 수 있는 이름으로 보여 줍니다.

dSYM 만들기

Release 구성의 Build Settings > Debug Information Format이 DWARF with dSYM File인지 확인합니다. 새 Xcode 프로젝트의 기본값입니다.

DEBUG_INFORMATION_FORMAT = dwarf-with-dsym

dSYM이 생기는 위치는 빌드 방법에 따라 다릅니다.

빌드 방법dSYM 위치
Xcode Archive / xcodebuild archive<이름>.xcarchive/dSYMs/
Build Phase 스크립트 안$DWARF_DSYM_FOLDER_PATH
fastlane gym<출력 디렉터리>/<앱>.app.dSYM.zip

CAUTION — dSYM은 빌드마다 다릅니다

같은 소스를 다시 빌드해도 UUID가 바뀝니다. App Store에 올린 바로 그 빌드의 dSYM을 올려야 합니다. 배포할 아카이브를 만든 CI 작업에서 함께 업로드하세요.

업로드

업로드에는 앱 단위 액세스 토큰이 필요합니다. 설정 > 프로젝트 > 앱 > 액세스 토큰에서 발급하세요. 토큰은 한 번만 표시됩니다.

@sophonz/cli

--file에 .dSYM 번들, 번들이 여러 개 들어 있는 디렉터리, 또는 zip을 넘깁니다. 디렉터리를 넘기면 안의 .dSYM을 모두 찾아 zip 하나로 묶어 올립니다.

export SOPHONZ_TOKEN=sophonz_pat_...
 
npx @sophonz/cli upload-dsym \
  --file build/MyApp.xcarchive/dSYMs \
  --version 1.4.0 \
  --build-id 120
옵션설명
-f, --file.dSYM 번들, 번들이 든 디렉터리, 또는 zip (필수)
-v, --version앱 버전. CFBundleShortVersionString(MARKETING_VERSION)
-b, --build-id빌드 번호. CFBundleVersion(CURRENT_PROJECT_VERSION)
-t, --token액세스 토큰. 기본값은 SOPHONZ_TOKEN
--dry-run올릴 대상만 출력

--version과 --build-id 중 하나는 있어야 합니다. SDK는 CFBundleShortVersionString을 service.version으로 보내므로 --version에 같은 값을 넣으면 설정의 버전 목록에서 같은 버전 아래에 표시됩니다.

GitHub Actions

- name: Archive
  run: |
    xcodebuild archive \
      -scheme MyApp -configuration Release \
      -archivePath build/MyApp.xcarchive
 
- name: Upload dSYMs
  env:
    SOPHONZ_TOKEN: ${{ secrets.SOPHONZ_TOKEN }}
  run: |
    PLIST=build/MyApp.xcarchive/Products/Applications/MyApp.app/Info.plist
    npx @sophonz/cli upload-dsym \
      --file build/MyApp.xcarchive/dSYMs \
      --version "$(/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' "$PLIST")" \
      --build-id "$(/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' "$PLIST")"

Xcode Build Phase

로컬 Archive에서도 올리려면 타깃의 Build Phases에 Run Script를 추가합니다. 빌드 머신에 Node.js 20 이상이 있어야 하고, 토큰은 저장소에 커밋하지 않도록 환경에서 읽습니다.

if [ "$CONFIGURATION" = "Release" ] && [ -n "$SOPHONZ_TOKEN" ]; then
  npx @sophonz/cli upload-dsym \
    --file "$DWARF_DSYM_FOLDER_PATH" \
    --version "$MARKETING_VERSION" \
    --build-id "$CURRENT_PROJECT_VERSION"
fi

Run Script의 Input Files에 ${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}을 넣어 dSYM이 만들어진 뒤 실행되게 합니다.

curl

zip으로 묶은 dSYM을 직접 올릴 수도 있습니다.

cd build/MyApp.xcarchive/dSYMs && zip -r ../../dSYMs.zip . && cd -
 
curl -X POST https://app.sophonz.ai/api/v1/mapping-files \
  -H "Authorization: Bearer $SOPHONZ_TOKEN" \
  -F type=DSYM \
  -F version=1.4.0 \
  -F buildId=120 \
  -F file=@build/dSYMs.zip

매칭 방식

dSYM은 올라오는 즉시 안에 든 Mach-O 이미지의 UUID가 기록됩니다. 스택 트레이스를 열면 프레임의 이미지 UUID와 같은 UUID를 가진 dSYM을 찾아 복원합니다.

  • 앱과 프레임워크를 따로 올려도, 한 zip에 함께 올려도 됩니다.
  • UUID로 찾으므로 버전 문자열이 달라도 같은 빌드면 매칭됩니다. 버전은 목록 표시와 관리를 위한 값입니다.
  • UUID가 들어 있지 않은 파일은 422 mappingInvalid로 거절됩니다.

확인

설정 > 프로젝트 > 앱 > 매핑 파일에 앱의 버전 목록이 있습니다. 목록은 텔레메트리에서 자동으로 채워지고, 버전을 펼치면 올라온 dSYM과 이미지 UUID가 보입니다. 텔레메트리를 보냈는데 dSYM이 없는 버전에는 경고가 표시됩니다. 출시 전 버전은 버전 추가로 먼저 만들어 둘 수 있습니다.

크래시나 에러가 들어오면 로그나 트레이스에서 행을 펼칩니다. 스택 트레이스 뷰어는 Xcode 디버그 내비게이터와 비슷하게 구성됩니다.

  • 크래시 원인, 신호, 주소가 맨 위에 표시됩니다.
  • 스레드 목록에서 크래시가 난 스레드가 맨 위에 옵니다. 앱 프레임이 있는 스레드만 필터를 켤 수 있습니다.
  • 앱 바이너리의 프레임은 강조되고, 연속된 시스템 프레임은 접혀서 보입니다.
  • 인라인된 함수는 들여쓴 하위 행으로 펼쳐집니다.
  • 바이너리 이미지, 레지스터, 원문 탭이 있습니다. 복원 / 원본 전환으로 기기가 보낸 그대로의 프레임도 볼 수 있습니다.

업로드 API

POST https://app.sophonz.ai/api/v1/mapping-files
Authorization: Bearer sophonz_pat_...
Content-Type: multipart/form-data
필드설명
typeDSYM
filedSYM zip, 또는 DWARF 바이너리 하나
version앱 버전
buildId빌드 번호 (선택)

version과 buildId 중 하나는 필수입니다. 성공하면 201과 함께 기록된 UUID를 돌려줍니다.

{
  "file": {
    "id": "cmu1...",
    "type": "DSYM",
    "version": "1.4.0",
    "buildId": "120",
    "fileName": "dSYMs.zip",
    "debugIds": ["D8D5EDD1-CD2A-32DE-8184-0E016F60AAFB"]
  }
}
상태error원인
401unauthorized토큰이 없거나 잘못됐거나 만료·폐기됨
400fileMissing / typeInvalid / versionRequired필드 누락
413fileTooLarge500MB 초과
422mappingInvalidUUID가 있는 Mach-O 이미지를 찾지 못함
500storageFailed서버가 파일을 저장하지 못함