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-dsymdSYM이 생기는 위치는 빌드 방법에 따라 다릅니다.
| 빌드 방법 | 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"
fiRun 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| 필드 | 설명 |
|---|---|
type | DSYM |
file | dSYM 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 | 원인 |
|---|---|---|
401 | unauthorized | 토큰이 없거나 잘못됐거나 만료·폐기됨 |
400 | fileMissing / typeInvalid / versionRequired | 필드 누락 |
413 | fileTooLarge | 500MB 초과 |
422 | mappingInvalid | UUID가 있는 Mach-O 이미지를 찾지 못함 |
500 | storageFailed | 서버가 파일을 저장하지 못함 |