当咱们打包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,欢迎你们指教共同进步!给个关注最好啦~