2013年9月2日 星期一

Crash and dSYM

因為近來的工作需要,所以開始著手研究了一下exception的問題,其實在iOS中,APPLE非常人性化的在APP Crash的時候,會自動在手機裡面生成一個.crash file 給我們。

而通常我們拿到這個檔案,如果單純從客戶端那邊看(沒有 .dSYM 檔的話) 是會看到以下的情形

.Crash File
// 1:Thread 資訊
Incident Identifier: CAF9ED40-2D59-45EA-96B0-52BDA1115E9F
CrashReporter Key:   30af939d26f6ecc5f0d08653b2aaf47933ad8b8e
Process:         TestEditor [12506]
Path:            /var/mobile/Applications/60ACEDBC-600E-42AF-9252-42E32188A044/TestEditor.app/TestEditor
Identifier:      TestEditor
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]
// 2:基本資訊
Date/Time: 2013-09-23 11:25:56.357 +0900 OS Version: iPhone OS 3.1.3 (7E18) Report Version: 104
// 3:例外
Exception Type: EXC_BAD_ACCESS (SIGBUS) Exception Codes: KERN_PROTECTION_FAILURE at 0x00000059 Crashed Thread: 0
// 4:Back Trace
Thread 0 Crashed: 0 UIKit 0x332b98d8 0x331b2000 + 1079512 1 UIKit 0x3321d1a8 0x331b2000 + 438696 2 UIKit 0x3321d028 0x331b2000 + 438312 3 UIKit 0x332b9628 0x331b2000 + 1078824 4 UIKit 0x33209d70 0x331b2000 + 359792 5 UIKit 0x33209c08 0x331b2000 + 359432 6 QuartzCore 0x324cc05c 0x324ae000 + 122972 7 QuartzCore 0x324cbe64 0x324ae000 + 122468 8 CoreFoundation 0x3244f4bc 0x323f8000 + 357564 9 CoreFoundation 0x3244ec18 0x323f8000 + 355352 10 GraphicsServices 0x342e91c0 0x342e5000 + 16832 11 UIKit 0x331b5c28 0x331b2000 + 15400 12 UIKit 0x331b4228 0x331b2000 + 8744 13 TestEditor 0x00002c3a 0x1000 + 7226 14 TestEditor 0x00002c04 0x1000 + 7172
// 5:Thread State
Thread 0 crashed with ARM Thread State (32-bit):
    r0: 0x00000000    r1: 0x00000000      r2: 0x00000001      r3: 0x39529fc8
    r4: 0xffffffff    r5: 0x2fd7d301      r6: 0x2fd7d300      r7: 0x2fd7d9d0
    r8: 0x2fd7d330    r9: 0x3adbf8a8     r10: 0x2fd7d308     r11: 0x00000032
    ip: 0x00000025    sp: 0x2fd7d2ec      lr: 0x001bdb25      pc: 0x30301838
  cpsr: 0x00000010
// 6:二進制映像
Binary Images: 0x1000 - 0x7fff +TestEditor armv7 /var/mobile/Applications/60ACEDBC-600E-42AF-9252-42E32188A044/TestEditor.app/TestEditor

我們可以看到4:Back Trace 那邊,後面的記憶體位置 對我們而言沒任何的幫助。

因此,如果遇到這種情況的話,我們就必須將這些memory address 丟到memory stack 下去尋找。
而apple也提供了我們一個很好用的工具 symbolicatecrash 我們只需將 crash file與 dSYM file一併給他,就可以得到以下的結果

格式 : symbolicatecrash [crash file] [dSYM file]

Thread 0 Crashed:
0   UIKit                           0x332b98d8 -[UIWindowController transitionViewDidComplete:fromView:toView:] + 668
1   UIKit                           0x3321d1a8 -[UITransitionView notifyDidCompleteTransition:] + 160
2   UIKit                           0x3321d028 -[UITransitionView _didCompleteTransition:] + 704
3   UIKit                           0x332b9628 -[UITransitionView _transitionDidStop:finished:] + 44
4   UIKit                           0x33209d70 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 284
5   UIKit                           0x33209c08 -[UIViewAnimationState animationDidStop:finished:] + 60
6   QuartzCore                      0x324cc05c _ZL23run_animation_callbacksdPv + 440
7   QuartzCore                      0x324cbe64 _ZN2CAL14timer_callbackEP16__CFRunLoopTimerPv + 156
8   CoreFoundation                  0x3244f4bc CFRunLoopRunSpecific + 2192
9   CoreFoundation                  0x3244ec18 CFRunLoopRunInMode + 44
10  GraphicsServices                0x342e91c0 GSEventRunModal + 188
11  UIKit                           0x331b5c28 -[UIApplication _run] + 552
12  UIKit                           0x331b4228 UIApplicationMain + 960
13  TestEditor                      0x00002c3a main (main.m:14)
14  TestEditor                      0x00002c04 start


這時候對應adress的method name全都出來了!!
但是相信這時候應該很多人會有疑問,symbolicatecrash 在哪裡呢? .dSYM file 又在哪裡呢?
Symbolicatecrash:(預設location)
/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash

通常會將Symbolicatecrash檔案,複製到/usr/bin/ 裡面,好讓Terminate可以直接執行呼叫
sudo cp /Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash /usr/local/bin

.dSYM檔基本上預設是放在 
{TARGET_BUILD}/TestEditor.app.dSYM

但是通常的情況下,是不需要有這些動作的,因為XCODE在開啟的時候,預設是會去驗證相同的UUID下的dSYM檔,而自動幫你轉好。

難道以上的都是廢話嗎?
這倒不至於,為了避免發生dSYM檔案遺失而照成mapping不到symbole,可以參照這段sh file,在release並且實機的環境下,先將dSYM複製一份到你的專案上,再用以上的方法,自行輸出產生一個LOG FILE。

另外再補充一點
atos -arch armv7 -o app name.app/app name 0x0003b508
armv7,這個要取決於你的arm的指令集,這邊也有可能是armv6, armv7s之類的,.crash file裡面有提供訊息。
app name.app/app name則是要填入你build完的app。
0x0003b508,這個則是要填入相對的address。
什麼是相對的address?
address = slide Address + crash Address - image Address

slide Address 通常是0x1000,或是可用指令取得。
image Address 就是 binary image裡面對應的memory sector的起始位址。

而驗證app 與 crash 檔的UUID的指令為
xcrun dwarfdump --uuid ${dSYMInput} | tr '[:upper:]' '[:lower:]' | tr -d '-'
(這行會印出uuid 與 指令集)
grep {uuid} {.crash} (uuid為上述看到的uuid值,.crash為實體檔案)
補上 將dSYM以及app 檔案複製一份到專案的sh file

#!/bin/sh
if [ "${CONFIGURATION}" = "Debug" ]; then
echo “Skipping debug”
exit 0;
fi
if [ "$EFFECTIVE_PLATFORM_NAME" == "-iphonesimulator" ]; then
echo “Skipping simulator build”
exit 0;
fi
mkdir -p "${PROJECT_DIR}/dSYM"
mkdir -p "${PROJECT_DIR}/dSYM/$(date +%Y%m%d)"
SRC_PATH=${TARGET_BUILD_DIR}/${DWARF_DSYM_FILE_NAME}
SRC_PATH_APP=${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}
#UUID Path
RELATIVE_DEST_PATH_UUID=dSYM/$(date +%Y%m%d)/${EXECUTABLE_NAME}_UUID
DEST_PATH_UUID=${PROJECT_DIR}/${RELATIVE_DEST_PATH_UUID}
rm -f ${DEST_PATH_UUID}
xcrun dwarfdump --uuid ${SRC_PATH_APP} | tr '[:upper:]' '[:lower:]' | tr -d '-' | tr -d '(' | tr -d ')' > ${DEST_PATH_UUID}
exec < $DEST_PATH_UUID
while read -a LINE; do
    UUID_NAME="${LINE[1]}_${LINE[2]}"
done
#dSYM Path
RELATIVE_DEST_PATH=dSYM/$(date +%Y%m%d)/${EXECUTABLE_NAME}.${UUID_NAME}.app.dSYM
DEST_PATH=${PROJECT_DIR}/${RELATIVE_DEST_PATH}
#APP Path
RELATIVE_DEST_PATH_APP=dSYM/$(date +%Y%m%d)/${EXECUTABLE_NAME}.${UUID_NAME}.app
DEST_PATH_APP=${PROJECT_DIR}/${RELATIVE_DEST_PATH_APP}
echo "moving ${SRC_PATH} to ${DEST_PATH}"
echo "moving ${SRC_PATH_APP} to ${DEST_PATH_APP}"
cp -r "${SRC_PATH}" "${DEST_PATH}"
cp -r "${SRC_PATH_APP}" "${DEST_PATH_APP}"

下一章 我們要來分析 如何從上述的.crash裡面劃分的六大項,來取得發生crash的地方。



沒有留言:

張貼留言