Unity Native崩溃堆栈解析

Unity Native崩溃堆栈解析


前言

最近在工作中查询线上崩溃的时候发现Bugly上有些崩溃日志无法还原,即使上传了对应的符号表也无济于事。为了还原崩溃日志,定位崩溃原因,学习了一下在此记录。


崩溃日志

接入Bugly之后,手机上崩溃前的崩溃日志会被上传到Bugly上。开发人员可以在Bugly的“崩溃分析”页面中查看崩溃日志。

有些崩溃日志Bugly无法检测到能够还原的部分,也就是说即使上传了符号表,Bugly没法识别日志,也就无法自动通过上传的符号表还原日志。

比如下图崩溃:

Unity Native崩溃堆栈解析_第1张图片

这个时候,开发人员想定位崩溃原因,就需要手动还原崩溃信息。需要关注的内容,就是上图backtrace部分的内容。(这里只是Bugly上一种backtrace的表现形式,还有很多种表现形式,如#01 pc 000402b1 /system/lib/libc.so (pthread_kill+32) [armeabi-v7a]等)


Unity Native堆栈解析

所需文件

如果我们想要在本地手动解析/还原崩溃信息,那么就需要两个文件:

1、libunity.sym.so,即Unity的libunity库符号表。

2、addr2line,即Native堆栈解析工具。


libunity.sym.so

Unity在5.3之后就提供了libunity和libmain的符号表。

符号表的位置就在Unity路径下,根据不同的ARM架构有不同的libunity符号表。

Mac上的路径示例如下:

/…/Unity/PlaybackEngines/AndroidPlayer/Variations/il2cpp/Release/Symbols/armeabi-v7a/libunity.sym.so

/…/Unity/PlaybackEngines/AndroidPlayer/Variations/il2cpp/Release/Symbols/arm64-v8a/libunity.sym.so


注意:生成apk的Unity版本和libunity符号表的Unity版本必须一致,否则解析的内容会不正确。


addr2line

NDK中自带addr2line工具,可以通过读取符号表来解析崩溃堆栈。里面包括各种CPU架构版本,使用时需要选择对应的版本。一般来说,Android使用arm-linux-androideabi-4.9即可。

Mac上的路径示例如下:

/…/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line

/…/android-ndk-r16b/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line


注意:最好使用生成apk的Unity所使用的NDK中的addr2line工具。


使用方法

addr2line所支持的参数:

addr2line [-a| --addresses ] [-b bfdname | --target=bfdname] [-C | --demangle[=style]] [-e filename | --exe=filename] [-f | --function] [-s | --basename] [-i | --inlines] [-p | --pretty-print] [-j | --section=name] [-H | --help] [-V | --version] [addr addr ...]
-a --addresses:在函数名、文件和行号信息之前,显示地址,以十六进制形式。
-b --target=<bfdname>:指定目标文件的格式为bfdname。
-e --exe=<executable>:指定需要转换地址的可执行文件名。
-i --inlines : 如果需要转换的地址是一个内联函数,则输出的信息包括其最近范围内的一个非内联函数的信息。
-j --section=<name>:给出的地址代表指定section的偏移,而非绝对地址。
-p --pretty-print:使得该函数的输出信息更加人性化:每一个地址的信息占一行。
-s --basenames:仅仅显示每个文件名的基址(即不显示文件的具体路径,只显示文件名)。
-f --functions:在显示文件名、行号输出信息的同时显示函数名信息。
-C --demangle[=style]:将低级别的符号名解码为用户级别的名字。
-h --help:输出帮助信息。
-v --version:输出版本号。

使用范例:

假设崩溃backtrace的堆栈的pc寄存器内容按顺序如下:

001897d0 0019cfb0 004d1c80 00654114 00652128 0067a464 0067b5e0 0067b790 005c6964 0026868c 005c897c 005c87d0 005c875c 00603c60

在终端中执行如下命令:

/Users/apple/Downloads/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line -f -C -e /Applications/Unity/PlaybackEngines/AndroidPlayer/Variations/il2cpp/Release/Symbols/armeabi-v7a/libunity.sym.so 001897d0 0019cfb0 004d1c80 00654114 00652128 0067a464 0067b5e0 0067b790 005c6964 0026868c 005c897c 005c87d0 005c875c 00603c60

可以获得结果:

core::StringStorageDefault<char>::resize(unsigned int, bool)
??:?
void SafeBinaryRead::TransferSTLStyleArray<core::basic_string<char, core::StringStorageDefault<char> > >(core::basic_string<char, core::StringStorageDefault<char> >&, TransferMetaFlags)
??:?
void NamedObject::Transfer<SafeBinaryRead>(SafeBinaryRead&)
??:?
void MonoScript::Transfer<SafeBinaryRead>(SafeBinaryRead&)
??:?
MonoScript::VirtualRedirectTransfer(SafeBinaryRead&)
??:?
SerializedFile::ReadObject(long long, ObjectCreationMode, bool, TypeTree const**, bool*, Object&)
??:?
PersistentManager::ReadAndActivateObjectThreaded(int, SerializedObjectIdentifier const&, SerializedFile*, bool, bool, PersistentManager::LockFlags)
??:?
PersistentManager::LoadObjectsThreaded(int const*, int, LoadProgress&, bool, PersistentManager::LockFlags)
??:?
LoadOperation::Perform()
??:?
AssetBundleLoadAssetOperation::Perform()
??:?
PreloadManager::ProcessSingleOperation()
??:?
PreloadManager::Run()
??:?
PreloadManager::Run(void*)
??:?
Thread::RunThreadWrapper(void*)
??:?

Native崩溃堆栈信息就这样还原了。


后记

希望后人少踩坑,没啥别的了,加油就完事了!


参考

https://support.unity.com/hc/zh-cn/articles/115000292166-%E4%BD%BFAndroid%E5%A5%94%E6%BA%83%E6%97%A5%E5%BF%97%E7%AC%A6%E5%8F%B7%E5%8C%96

https://blog.csdn.net/linxinfa/article/details/107681049

https://blog.csdn.net/Sparrowfc/article/details/78384248

你可能感兴趣的:(Unity3D技术分享,unity3d,unity)