iOS Crash日志
Understanding Crash Reports on iPhone OS
https://developer.apple.com/videos/wwdc/2010/?id=317
http://www.cnblogs.com/smileEvday/p/Crash1.html
http://www.cocoachina.com/industry/20130725/6677.html
http://www.cnblogs.com/tiechui/p/3820044.html (http://developer.apple.com/library/ios/#technotes/tn2151/_index.html)
当一个应用程序在一台iOS 设备上崩溃时,一份“崩溃报告”将在该设备上次创建并存储起来。崩溃报告描述应用程序是在何种条件下崩溃的,大部分情况下包含一份当前正在运行线程的完整的堆栈跟踪。
产生崩溃日志的原因
- 应用违反操作系统规则,包括在启动、恢复、挂起、退出时watchdog超时、用户强制退出和低内存终止等。
- 应用中有Bug
从多任务窗口中终止一个暂停的应用程序不会产生崩溃日志。一旦一个应用被暂停,它有资格被iOS在任何时间终止,因此不会产生崩溃日志。
Crash获取途径
- 本机通过Xcode的Devices窗口获取某个设备的崩溃日志
- 设备与电脑上的iTunes Store同步后,会将崩溃日志保存在电脑上。根据电脑操作系统的不同,崩溃日志将保存在以下位置:
- Mac OS X: ~/Library/Logs/CrashReporter/MobileDevice/
- Windows XP: C:/Documents and Settings/Application Data/Apple Computer/Logs/CrashReporte/rMobileDevice/
- Windows Vista以上: C:/Users/用户名/AppData/Roaming/Apple Computer/Logs/CrashReporter/MobileDevice/
- 应用提交到App Store后,可通过itunes connect后台获取到用户上报的Crash日志。
- 有很多优秀的第三方Crash收集系统大大的方便了我们收集Crash,甚至还带了符号化Crash日志的功能。比较常用的有Crashlytics,Flurry等。
Crash文件结构
1.Process Information
Incident Identifier: 66AFBBF0-7ACB-4319-97C7-6F44E09FF9EB //崩溃报告的唯一标识符 CrashReporter Key: 97aec51145730a778c0d1cfdfc17c1b8c86ba4c5 //设备标识相对应的唯一键值(并非真正的设备的UDID,为保护隐私iOS6以后已无法获取) Hardware Model: iPad5,3 //发生Crash的设备类型 Process: XXXXClient [407] //Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID Path: /private/var/mobile/Containers/Bundle/Application/0380D606-3A40-4633-A1B2-7E1F3E3D4FCA/XXXXClient.app/XXXXClient
//可执行程序在手机上的存储位置,注意路径时到x.app/x,x.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的x Identifier: com.xxxx.myapp //App的Indentifier,通常为“com.xxx.yyy” Version: 1 (1.0.0) //App的版本号,由Info.plist中CFBundleShortVersionString +
CFBundleVersion
Code Type: ARM-64 (Native) //App的CPU架构 Parent Process: launchd [1] //当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd
2.Basic Information
Date/Time: 2016-02-19 00:34:43.449 -0800 //Crash发生的时间 Launch Time: 2016-02-19 00:34:43.399 -0800 OS Version: iOS 8.4 (12H143) //系统版本,括号内的数字代表的时Bulid号 Report Version: 105 //Crash日志的格式
3.Exception
Exception Type: EXC_CRASH (SIGABRT) //异常类型
Exception Subtype: //v104 Exception Codes: 0x0000000000000000, 0x0000000000000000 //v105 Triggered by Thread: 0 //v105
Crashed Thread //v104
4.Thread Backtrace
发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。
Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed:
//编号 二进制库名 调用方法的地址 基本地址 + 偏移 0 libsystem_kernel.dylib 0x0000000194b3b270 __pthread_kill + 8 1 libsystem_pthread.dylib 0x0000000194bd916c pthread_kill + 108 2 libsystem_c.dylib 0x0000000194ab2b14 abort + 108 3 ...g_rt.asan_ios_dynamic.dylib 0x00000001032756d0 0x103224000 + 333520 4 ...g_rt.asan_ios_dynamic.dylib 0x000000010326955c 0x103224000 + 283996 5 ...g_rt.asan_ios_dynamic.dylib 0x000000010326cf28 0x103224000 + 298792 6 ...g_rt.asan_ios_dynamic.dylib 0x0000000103269640 0x103224000 + 284224 7 ...g_rt.asan_ios_dynamic.dylib 0x000000010326d0e8 0x103224000 + 299240 8 ...g_rt.asan_ios_dynamic.dylib 0x000000010325ef50 0x103224000 + 241488 9 ...g_rt.asan_ios_dynamic.dylib 0x0000000103268d18 0x103224000 + 281880 10 dyld 0x00000001200b9234 ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 256 11 dyld 0x00000001200b93ec ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 32 12 dyld 0x00000001200b5688 ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 328 13 dyld 0x00000001200b561c ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 220 14 dyld 0x00000001200b54d8 ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 136 15 dyld 0x00000001200b57a0 ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 80 16 dyld 0x00000001200aa150 dyld::initializeMainExecutable() + 196 17 dyld 0x00000001200ad8bc dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 2664 18 dyld 0x00000001200a9040 _dyld_start + 64
5.Thread State
Crash时发生时刻,线程的状态(寄存器中的值)
Thread 0 crashed with ARM Thread State (64-bit): x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000010000 x4: 0x000000000000027b x5: 0x0000000000000000 x6: 0x0000000000000000 x7: 0x0000000000000250 x8: 0x0000000008000000 x9: 0x0000000004000000 x10: 0x0000000000000000 x11: 0x0000000000000018 x12: 0x0000000000000001 x13: 0x0000000000062aa8 x14: 0x0000000000000015 x15: 0x0000000000000000 x16: 0x0000000000000148 x17: 0x0000000000000000 x18: 0x0000000000000000 x19: 0x0000000000000006 x20: 0x0000000198ae4310 x21: 0x0000000103280963 x22: 0x0000000000000000 x23: 0x0000000000000000 x24: 0x0000000000000093 x25: 0x000000016fd182d8 x26: 0x00000001200d8d11 x27: 0x000000010328c000 x28: 0x00000001032243d0 fp: 0x000000016fd17920 lr: 0x0000000194bd9170 sp: 0x000000016fd17900 pc: 0x0000000194b3b270 cpsr: 0x00000000
6.Binary Images
Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行文件的包得uuid位c0f……cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。
Binary Images: 0x1000e4000 - 0x101fdffff XXXClient arm64 <aa8ef7e9f9c43c7c87f4f75cf266d479> /var/mobile/Containers/Bundle/Application/0380D606-3A40-4633-A1B2-7E1F3E3D4FCA/XXXXClient.app/XXXXClient 0x103224000 - 0x103287fff libclang_rt.asan_ios_dynamic.dylib arm64 <c51061e5b8443a8e9b6c2b76628b4b95> /var/mobile/Containers/Bundle/Application/0380D606-3A40-4633-A1B2-7E1F3E3D4FCA/XXXX.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib 0x1200a8000 - 0x1200cffff dyld arm64 <de589e6153453237a6cf724cb236d83c> /usr/lib/dyld 0x1810ac000 - 0x181240fff AVFoundation arm64 <b9c4b32ba43a3a798c4adcaad3608f52> /System/Library/Frameworks/AVFoundation.framework/AVFoundation 0x181244000 - 0x1812a8fff libAVFAudio.dylib arm64 <6667f63f0f1635668dc941d6b79062e1> /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib 0x194bf0000 - 0x194bf5fff libunwind.dylib arm64 <8b87982b31ad3569a95e75457cadba3e> /usr/lib/system/libunwind.dylib 0x194bf8000 - 0x194c1bfff libxpc.dylib arm64 <c9f3c08a8a3b3849a905d24911240853> /usr/lib/system/libxpc.dylib
符号化
包含堆栈跟踪的崩溃报告需要先进行符号化(symbolicated)才可以进行分析。符号化的过程是将内存地址替换为便于人们阅读的函数名称和行号。假如你通过Xcode的Organizer窗口获取崩溃日志,那么该报告将在几秒钟后自动进行符号化。否则你需要将.crash文件导入到Xcode的Organizer进行符号化。
//符号化前 6 Rage Masters 0x0001625c 0x2a000 + 3003 //符号化后 6 Rage Masters 0x0001625c -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:35)
符号化与归档
Xcode符号化崩溃日志时,需要访问与App Store上对应的应用二进制文件以及生成二进制文件时产生的 .dSYM 文件。必需完全匹配才行。否则,日志将无法被完全符号化。所以,保留每个分发给用户的编译版本非常重要。提交应用前进行归档时,Xcode将保存应用的二进制文件。可以在Xcode Organizer的Archives标签栏下找到所有已归档的应用文件。在发现崩溃日志时,如果有相匹配的.dSYM文件和应用二进制文件,Xcode会自动对崩溃日志进行符号化。如果你换到别的电脑或创建新的账户,务必将所有二进制文件移动到正确的位置,使Xcode能找到它们。
(注意: 你必需同时保留应用二进制文件和.dSYM文件才能将崩溃日志完整符号化。每次提交到iTunes Connect的构建都必需归档。.dSYM文件和二进制文件是特定绑定于每一次构建和后续构建的,即使来自相同的源代码文件,每一次构建也与其他构建不同,不能相互替换。如果你使用Build 和 Archive 命令,这些文件会自动放在适当位置。 如果不是使用Build 和 Archive命令,放在Spotlight能够搜索到的位置(比如Home目录)即可。)
在崩溃报告中最令人感兴趣的部分是在你的应用程序执行终止时的堆栈跟踪。这个跟踪和你在调试器中停止执行时的类似,但遗憾的是这里没有提供被认为是符号的方法或函数的名称。取而代之的是16位的内存地址和它所指向的你的应用程序或系统框架的可执行代码。你需要将这些地址映射到符号中。崩溃日志在输出时并不包含土豪信息,你需要在你分析日志前进行符号化处理。
符号化——将堆栈跟踪地址转化为源代码方法名称及行号——需要上次到苹果应用商店的应用程序的二进制文件以及构建二进制文件时产生的.dSYM文件。这些必须是精确匹配的,否则你的报告将不能完整地符号化。基本要求你保持每个分发给用户(忽略那些分发的细节)的构建和它的.dSYM是一致的。
注意:
你必须保存应用程序的二进制文件和.dSYM文件以便于完整地符号化崩溃日志。你应该打包每一个你所提交到iTunes Connect中的构建。.dSYM文件和应用程序的二进制文件应该绑定到一起,不管是基础版本的构建还是后续版本的构建。即便是相同的源代码,不同构建的文件也不会弄混。假如你使用“构建并打包”命令,那么它们将会被自动放置到一个合适的位置。虽然任何位置都可以用Spotlight搜索到。
Xcode的“打包(Archive)”命令使保持二进制文件和.dSYM匹配变得很简单,当你使用打包命令(通过点击产品(“Product”)-)打包(Archive)或是按下Shift+Command+B),Xode将会把应用程序的二进制文件及包含符号信息的.dSYM文件收集到一起,并存储到你的主目录文件夹下的一个合适位置。你可以在Xcode中的Organizer下的“已打包(Archived)”中知道你所打包的所有应用程序。Xcode在符号化崩溃日志时会自动寻找打包的应用程序,在确认你要的发布应用程序和.dSYM文件匹配的情况下,你可以将它们打包,直接提交到ITunes Connect。
如果Xcode拥有产生崩溃日志的应用程序的二进制代码和.dSYM文件,那么它将自动进行符号化。Xcode Organizer符号化所需要提供的是崩溃日志及相应的二进制文件和.dSYM文件。打开Xcode Organizer,选择“设备(Devices)”选选看,选择边栏顶部“文库(LIBRARY)”下的“设备日志(Device Logs)”,点击导入按钮,选择需要符号化的.crash文件,当这些步骤完成后,Xcode将自动符号化崩溃日志并显示结果。
常见Crash类型
0x8badf00d(eat bad food) Watchdog timeout
紧接着下面会有一段描述:
Application Specific Information:
com.xxx.yyy failed to resume in time
该应用程序花了太长的时间加载,终止或响应系统时间。如果你没有把需要花费时间比较长的操作(如网络访问)放在后台线程上就很容易发生这种情况。
从iOS 4.x开始,退出应用时,应用不会立即终止,而是退到后台。但是,如果你的应用响应不够快,操作系统有可能会终止你的应用,并产生一个崩溃日志。下面这些方法,应用只有有限的时间去完成处理。如果花费时间太长,操作系统将终止应用。
application:didFinishLaunchingWithOptions:
对于此类Crash,我们应该去审视自己App初始化时做的事情是否正确,是否在主线程请求了网络,或者其他耗时的事情卡住了正常初始化流程。
通常系统允许一个App从启动到可以相应用户事件的时间最多为5S,如果超过了5S,App就会被系统终止掉。在Launch,resume,suspend,quit时都会有相应的时间要求。在Highlight Thread里面我们可以看到被终止时调用到的位置,xxxAppDelegate加上行号。
PS. 在连接Xcode调试时为了便于调试,系统会暂时禁用掉Watchdog,所以此类问题的发现需要使用正常的启动模式。
0xdeadfa11(dead fall) User force-quit
这个强制退出跟我们平时所说的kill掉后台任务操作还不太一样,通常在程序bug造成系统无法响应时可以采用长按电源键,当屏幕出现关机确认画面时按下Home键即可关闭当前程序。
双击Home按钮后,你将看到运行过的所有应用。那些应用不一定是正在运行,也不一定是被挂起。 通常,用户点击Home按钮时,应用将在后台保留约10分钟,然后操作系统自动将其终止。 所以双击Home按钮显示的应用列表只是表明那是一系列过去打开过的应用。删除那些应用的图标不会产生任何崩溃日志。
0xbaaaaaad
该Crash log并非一个真正的Crash,它仅仅只是包含了整个系统某一时刻的运行状态。通常可以通过同时按Home键和音量键,可能由于用户不小心触发
0xbad22222
当VOIP程序在后台太过频繁的激活时,系统可能会终止此类程序
0xc00010ff
程序执行大量耗费CPU和GPU的运算,导致设备过热,触发系统过热保护被系统终止
0xdead10cc
程序退到后台时还占用系统资源,如通讯录被系统终止
Low Memory termination
跟一般的Crash结构不太一样,通常有Free pages,Wired Pages,Purgeable pages,largest process 组成,同时会列出当前时刻系统运行所有进程的信息。IOS 内存警告 Memory warning level
App在运行过程中,系统内存紧张时通常会先发警告(子类化UIViewController时,你或许已经注意到didReceiveMemoryWarning方法),同时把后台挂起的程序终止掉,最终如果还是内存不够的话就会终止掉当前前台的进程。
当接受到内存警告的事后,我们应该释放尽可能多的内存,Crash其实也可以看做是对App的一种保护。
当监测到内存不足时,iOS的虚拟内存系统依靠应用程序间的合作来释放内存。内存不足的通知被发送到所有正在运行的应用程序,并作为内存释放的请求来进行处理,以期望降低所使用的内存总量。如果内存的压力始终存在,那么系统可能会终止一些后台进程来降低内存压力。如果可以释放足够多的内存,那么你的应用程序将继续运行而不会产生崩溃报告。如果不可以,那么你的程序将被iOS终止,因为此时已没有足够的内存来满足应用程序的需求,一份内存不足的报告将被产生并存储在设备中。
内存不足报告与其他崩溃报告的不同之处在于它里面没有应用程序的堆栈跟踪。每个进程的内存使用量依据内存页面的数量进行报告,每个内存页面量的大小为4KB。你将会看到“抛弃(jettisoned)”紧跟在iOS为了释放内存而终止的进程名称后。如果你看到它紧跟在你的应用程序名称后面,那么可以确定这个应用因为使用了太多的内存而被终止。否则,应该不是内存压力引起的崩溃。你可以通过查看.crash文件(下一节中进行描述)获取更多信息。
当你看到一个内存不足报告时,你更应该研究一下你使用内存的方式和你对内存不足警告的处理方式,而不是去关心在程序终止时你的哪一部分代码被执行了。内存分配帮助(Memory Allocations Help)列举了如何使用泄露工具(Leaks Instrument)来发现内存泄露,以及如何使用分配工具(Allocations Instrument's)的标记堆功能来避免被抛弃的内存。内存使用性能指导方案(Memory Usage Performance Guidelines)讨论了像其他内存使用秘诀一样的适当的方案来响应内存不足通知。同时也建议你看一下WWSC2010中的关于使用工具进行高效内存分析的视频(Advanced Memory Analysis with Instruments)。
泄露和分配工具不能跟踪显存。你需要使用VM Tracker工具(包含在分配工具模板中)来运行你的应用以便观察显存的使用情况。VM Tracker默认是禁用的。为了在你的应用程序使用VM Tracker,请点击工具,选中“自动快照Automatic Snapshotting”标志或者手工按下“获取快照(Snapshot Now)”按钮。
当应用发生低内存闪退时,你必需看看应用中内存使用的方式,以及是如何处理低内存警告的。你可以使用Instruments工具中使用Allocations 和 Leaks来发现内存分配问题和内存泄漏问题。如果你不知道如何利用 Instruments 检查内存问题,可以看看这个教程 。
当内存使用达到一定程度时,操作系统将发出一个 UIApplicationDidReceiveMemoryWarningNotification 通知。同时,调用 didReceiveMemoryWarning 方法。
Crash due to bugs
常见Exception Type
EXC_BAD_ACCESS 通常用于访问了不改访问的内存导致。一般EXC_BAD_ACCESS后面的"()"还会带有补充信息。
- SIGSEGV: 通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。
- SIGABRT: 收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。
- SEGV:(Segmentation Violation),代表无效内存地址,比如空指针,未初始化指针,栈溢出等;
- SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)
- SIGILL:尝试执行非法的指令,可能不被识别或者没有权限
EXC_BAD_INSTRUCTION
此类异常通常由于线程执行非法指令导致
EXC_ARITHMETIC
除零错误会抛出此类异常
------------------------------
AddressSenitizer引发的崩溃
iOS9可以正常启动。
iOS9以下版本设备连接调试可以正确启动,断开调试后启动崩溃,日志如下
Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Triggered by Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 libsystem_kernel.dylib 0x0000000197747270 __pthread_kill + 8 1 libsystem_pthread.dylib 0x00000001977e516c pthread_kill + 108 2 libsystem_c.dylib 0x00000001976beb14 abort + 108 3 ...g_rt.asan_ios_dynamic.dylib 0x00000001019d56d0 0x101984000 + 333520 4 ...g_rt.asan_ios_dynamic.dylib 0x00000001019c955c 0x101984000 + 283996 5 ...g_rt.asan_ios_dynamic.dylib 0x00000001019ccf28 0x101984000 + 298792 6 ...g_rt.asan_ios_dynamic.dylib 0x00000001019c9640 0x101984000 + 284224 7 ...g_rt.asan_ios_dynamic.dylib 0x00000001019cd0e8 0x101984000 + 299240 8 ...g_rt.asan_ios_dynamic.dylib 0x00000001019bef50 0x101984000 + 241488 9 ...g_rt.asan_ios_dynamic.dylib 0x00000001019c8d18 0x101984000 + 281880 10 dyld 0x0000000120095234 ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 256 11 dyld 0x00000001200953ec ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 32 12 dyld 0x0000000120091688 ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 328 13 dyld 0x000000012009161c ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 220 14 dyld 0x00000001200914d8 ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 136 15 dyld 0x00000001200917a0 ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 80 16 dyld 0x0000000120086150 dyld::initializeMainExecutable() + 196 17 dyld 0x00000001200898bc dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 2664 18 dyld 0x0000000120085040 _dyld_start + 64
http://stackoverflow.com/questions/32663540/run-app-deployed-with-xcode-7-gives-a-crash
更多关于AddressSenitizer
http://www.cocoachina.com/ios/20151020/13794.html
http://www.cnblogs.com/xitang/p/4904405.html