當咱們打包app時,能夠選擇生成對應的符號表,其保存 16 進制函數地址映射信息,經過給定的函數起始地址和偏移量,能夠對應函數具體信息以供分析。git
因此咱們拿到測試給的閃退日誌(.crash
)時,須要找到打包時對應生成的符號表(.dSYM
)做爲鑰匙解析。具體分爲下面幾個步驟github
dwarfdump --uuid
命令獲取 .dSYM
的 uuid
shell
打開 .crash
文件,在特定位置找到 uuid
bash
根據 arm
版本比對二者是否一致app
到 Xcode
目錄下尋找 symbolicatecrash
工具函數
不一樣版本文件路徑不一樣,具體版本請谷歌。Xcode9路徑是/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/工具
設置終端環境變量測試
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
ui
使用 symbolicatecrash
工具解析日誌 symbolicatecrash .crash .dsym > a.out
編碼
雖然過程不復雜,可是每次都須要手動執行一次檢查與命令,過於繁瑣,因此決定用腳本化提升效率。
#要求輸入crash文件路徑
inputFile 'Please Input Crash File' 'crash'
crashPath=$filePath
複製代碼
因爲須要輸入兩種不一樣後綴的文件路徑,且都須要檢查,所以統必定義一個方法。
#定義全局變量
filePath=
#輸入文件路徑
inputFile() {
readSuccess=false
#首先清空變量值
filePath=
while [ $readSuccess = false ]; do
echo $1
#讀取到變量中
read -a filePath
if [[ ! -e $filePath || ${filePath##*.} != $2 ]]; then
echo "Input file is not ."$2
else
readSuccess=true
fi
done
}
複製代碼
.dSYM
是文件夾路徑,因此這裏簡單的判斷了路徑是否存在,若是不存在就繼續讓用戶輸入。
Shell命令中判斷分爲[]與[[]],後者比前者更通用,可使用 || 正則運算等。
判斷中,-f表示檢查是否存在該文件,-d表示檢查是否存在文件夾,-e表示檢查是否存在該路徑
dsymSuccess=false
while [ $dsymSuccess = false ]; do
#要求輸入dSYM文件路徑
inputFile 'Please Input dSYM File' 'dSYM'
dsymPath=$filePath
#檢查是否匹配
checkUUID "$crashPath" "$dsymPath"
match=$?
if [ $match -eq 0 ]; then
echo 'UUID not match!'
else
dsymSuccess=true
fi
done
複製代碼
循環獲取匹配 UUID
的 dSYM
,這裏使用了另外一種方法獲取方法返回值,具體以後章節會總結。
在 Xcode
文件夾指定路徑下查找工具,加快效率,若是沒找到就中止運行。
# 查找symbolicatecrash解析工具,內置在Xcode的庫文件中
toolPath=`find /Applications/Xcode.app/Contents/SharedFrameworks -name symbolicatecrash | head -n 1`
if [ ! -f $toolPath ]; then
echo "Symbolicatecrash not exist!"
exit 0
fi
複製代碼
#先設置環境變量
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
#指定解析結果路徑
crashName=`basename $crashPath`
afterPath="$(dirname "$crashPath")"/"${crashName%%.*}""_after.crash"
#開始解析
$toolPath "$crashPath" "$dsymPath" > "$afterPath" 2> /dev/null
複製代碼
這裏我將錯誤信息導流到 /dev/null
,保證解析文件沒有雜亂信息。
以前沒有處理過須要返回數值的方法,因此一開始有點懵,查詢資料後最終採用了兩種方式實現了效果,如今作一些總結。
#定義全局變量
filePath=
inputFile() {
#讀取到變量中
read -a filePath
}
inputFile
crashPath=$filePath
複製代碼
經過 inputFile
方法來了解一下,首先定義一個全局變量爲 filePath
,在方法中從新賦值,方法結束後讀取全局變量中的數據。
這種方法的好處是能夠自定義返回參數類型和個數,缺點是容易和其餘變量搞混。
相似與C語言中的用法,腳本也支持 retrun 0
返回結果並中止運行。
checkUUID() {
grep "$arm64id" "$1"
if [ $? -ne 0 ]; then
return 1;
fi
return 0;
}
checkUUID "$crashPath" "$dsymPath"
match=$?
複製代碼
獲取結果的方式爲 $?
,其可以返回環境中最後一個指令結果,也就是以前執行的checkUUID
的結果。
優勢是簡潔明瞭,符合編碼習慣,缺點是返回值只能是 0-255
的數字,不能返回其餘類型的數據。
還有一種方法其實平時一直在使用,只不過並不瞭解其運行方式。
crashName=`basename $crashPath`
print() {
echo "Hello World"
}
text=$(print)
複製代碼
運行系統預設的方法或者自定義方法,將執行命令用 $()
的方式使用,就能夠獲取該命令中全部打印的信息,賦值到變量就能夠拿到須要的返回值。
優勢是功能全效率高,使用字符串的方式能夠傳遞定製化信息,缺點是不可預期返回結果,須要經過字符串查找等命令輔助。
在個人設想中,須要用戶輸入匹配的 dSYM
文件路徑,若是不匹配,則從新輸入,直到合法。爲了支持嵌套,須要定義局部變量控制循環,具體代碼以下
dsymSuccess=false
while [ $dsymSuccess = false ]; do
#要求輸入dSYM文件路徑
inputFile 'Please Input dSYM File' 'dSYM'
dsymPath=$filePath
#檢查是否匹配
checkUUID "$crashPath" "$dsymPath"
match=$?
if [ $match -eq 0 ]; then
echo 'UUID not match!'
else
dsymSuccess=true
fi
done
複製代碼
獲取到 UUID
全部輸出信息後,須要截取出對應平臺的信息,處理仍是不太熟悉,特意整理以下
#原始信息
UUID: 92E495AA-C2D4-3E9F-A759-A50AAEF446CD (armv7) /Volumes/.dSYM/Contents/Resources/DWARF/app
UUID: 536527A8-0243-34DB-AE08-F1F64ACA4351 (arm64) /Volumes/.dSYM/Contents/Resources/DWARF/app
#去除中間間隔-
uuid=${uuid//-/}
#從後往前找第一個匹配 \(arm64的,而且都刪除
arm64id=${uuid% \(arm64*}
#處理後
UUID: 92E495AAC2D43E9FA759A50AAEF446CD (armv7) /Volumes/.dSYM/Contents/Resources/DWARF/app
UUID: 536527A8024334DBAE08F1F64ACA4351
#從前日後找最後一個UUID: ,並刪除
arm64id=${arm64id##*UUID: }
#處理後
536527A8024334DBAE08F1F64ACA4351
複製代碼
看似簡單的腳本,也花了一天時間編寫,整體仍是不太熟練,仍需努力聯繫。
此次特意嘗試了與上次不一樣的參數輸入方法,使用提示輸入的方式,果真遇到了新的問題。好在都查資料解決了,結果還算滿意。
腳本我提交到了Github,歡迎你們指教共同進步!給個關注最好啦~