Crash Report 的三部分
一份crash report 大概分成三个部分。我们会用一份crash report 说明,这份crash report 来自于KKBOX 的QA,而且里头还没有解开(symbolication)-也就是说,从crash report 的call stack 这一段,我们只能够看到发生问题的function/method 的内存位置,而看不到错误发生在程式的哪一行。
环境摘要与错误主要原因
在crash report 的最上方可以看到一些基本资讯,其中比较重要的是
- 机种:在下面这份crash report 中,可以看到用户用的是iPad,同于KKBOX 的iOS 版本同时支援iPhone 与iPad,而在iPhone 与iPad 上根本有两套不一样的UI code,所以我们知道,现在应该要去看跟iPad 有关的问题。
- 版本号码:同一个App 的不同build,都会产生不一样的debug symbol,所以如果要找到正确的debug symbol,就一定要确认版号。 在KKBOX 的开发版本中,我们会对Info.plist 做一些pre-process,把Jenkins build 号码(是的,KKBOX 使用Jenkins 做持续整合,QA 都是直接从Jenkins 上抓取需要测试的build)与这一版程式的git revision hash 也加到版号中,所以,我们可以知道,应该要去Jenkins build 5145 里头去找debug symbol。
- 错误代码:我们在这边可以知道Exception Type 是EXC_CRASH,并且在main thread(thread 0)发生crash。 错误代码对我们理解crash 非常重要,我们会稍后说明。
Incident Identifier: CC5B3783-804F-49A7-AF6B-7C7B382CAE76
CrashReporter Key: 73124d372075eabb72b5625621a4396ffe893a49
Hardware Model: iPad2,5
Process: KKBOX [155]
Path: /private/var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/KKBOX
Identifier: tw.com.skysoft.iPhone
Version: 6.2.66.5145.073c882 (6.2.66)
Code Type: ARM (Native)
Parent Process: launchd [1]
Date/Time: 2015-08-06 10:41:52.859 +0800
Launch Time: 2015-08-06 10:39:28.377 +0800
OS Version: iOS 8.3 (12F69)
Report Version: 104
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread: 0
Call Stack
第二部分是每个thread 当时的call stack,让我们可以看到每个thread 当时在做些什么事情。这边会是了解问题的关键,因为我们要从这段资讯里头确实知道crash 发生在程式的哪个地方。
根据取得crash report 的方式不同,你可能会看到不同格式的资料。比方说,当你直接从装置上抓下来,你会看到完全没有解开的call stack,像这样:
Last Exception Backtrace:
(0x23e61132 0x321dec72 0x23e665f8 0x23e644d4 0x23d939d4 0x23e40272 0x24aefd96 0x24aefc46 0x755ba6 0x2776be1c 0x2776bede 0x27760a60 0x2756f2b6 0x27498c16 0x26eb7440 0x26eb2c90 0x26eb2b18 0x26eb24ba 0x26eb22aa 0x26f05ab8 0x2bad5bfe 0x24dd9d08 0x23e16550 0x23e26a46 0x23e269e2 0x23e25004 0x23d7099c 0x23d707ae 0x2b5201a4 0x274fb690 0xee070 0x32786aaa)
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x3284cdf0 0x32838000 + 85488
1 libsystem_pthread.dylib 0x328cdc92 0x328ca000 + 15506
2 libsystem_c.dylib 0x327eb934 0x327a2000 + 301364
3 KKBOX 0x00a12ee2 0x10000 + 10497762
4 KKBOX 0x00a6e620 0x10000 + 10872352
5 CoreFoundation 0x23e61466 0x23d57000 + 1090662
6 libobjc.A.dylib 0x321deefc 0x321d8000 + 28412
7 libc++abi.dylib 0x31a02dec 0x319eb000 + 97772
8 libc++abi.dylib 0x31a028b4 0x319eb000 + 96436
9 libobjc.A.dylib 0x321dedba 0x321d8000 + 28090
10 CoreFoundation 0x23d70a38 0x23d57000 + 105016
11 CoreFoundation 0x23d707ae 0x23d57000 + 104366
12 GraphicsServices 0x2b5201a4 0x2b517000 + 37284
13 UIKit 0x274fb690 0x2748c000 + 456336
14 KKBOX 0x000ee070 0x10000 + 909424
15 libdyld.dylib 0x32786aac 0x32785000 + 6828
如果你是在你自己电脑上,用Xcode 按下Run,执行你的App,因为Xcode 帮你保留了App 的debug symbol,所以在装置上发生crash 的时候,你用Xcode 的Devices 选单把crash report 抓进来的时候,Xcode 会帮你用debug symbol,完整解开整份crash report。
但如果一份crash report 是来自QA,在QA 的电脑上有Xcode,但是并没有对应的debug symbol 时,Xcode 也会尝试做symbolicate,但只能够解开像是UIKit、 CoreFoundation 之类的系统library,但是看不到属于你的App 的哪部分。而Xcode 是透过spotlight 的索引找到对应的debug symbol,就算把debug symbol 抓下来,但spotlight 不见得会立刻做索引,所以你可能还是看到没有解开的crash report。
解开系统library 的部份往往没什么用,以上面那个log 来说,如果你熟悉iOS 的运作,就算不解开记忆体位置,也可以看出thread 0 的call stack 中:
- 15 是
start
- 14 是KKBOX 的main.m 里头的
main
- 13 是UIApplicationMain
- 12-5 是在跑run loop,而由于这是一个exception,所以在thread 0 中跟KKBOX 有关的部份,大概是Google Analytics 或Hockey App 的exception handler
而我们在前面拉拉杂杂讲了许多基本观念,都是为了这一章做准备:我们要了解什么是run loop,才有办法了解这段call stack 的backtrace,对吧?
在这份crash report 中,其实我们更有兴趣的是Last Exception Backtrace 这一段。这段资料代表的是exception 发生当时到底发生了什么事,也就是exception 被throw 出来的阶段,而虽然crash report 告诉我们发生crash 的thread 0,但这时候thread 0 只是catch exception 的地方而已。
Libraries
第三部分是我们的App 载入了哪些library,因为很长,所以这边只放了部分。在这份log 中,最重要的资讯是KKBOX 被载入到0x10000 - 0xc1bfff 这一段记忆体的区间中,这个资讯对我们接下来怎么解开记忆体位置非常重要。
从一个App 载入了哪些library,也可以看出一些特色。比方说,KKBOX 除了载入自己的主要App 之外,也载入了在自己bundle 下的一份Swift runtime,像是libswiftCore.dylib、libswiftCoreAudio.dylib 等等,代表KKBOX 里头使用了Swift 语言。
我们在第一章就提到,Swift 与Objective-C 的runtime 实作不同,所以Swift 程式必须要连结到特定版本的Swift runtime,而用Xcode 开发出来的Swift App,会在每个App bundle 里头都放一份Swift runtime—那不就是装置里头有多少用Swift 写的App,装置上就放了多少份Swift runtime?的确如此,所以还好Swift 核心runtime 的尺寸不大,但我们也很担心以后Swift 会变得多大就是了。
Binary Images:
0x10000 - 0xc1bfff KKBOX armv7 <d91b937e9d073c21a79f5bbbe1cc4dbd> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/KKBOX
0x14e4000 - 0x164bfff libswiftCore.dylib armv7 <fa5b9494d6403f13ae80664a88301250> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftCore.dylib
0x17c0000 - 0x17c7fff libswiftCoreAudio.dylib armv7 <bd2181365f933ed3b330b0517f75fde0> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftCoreAudio.dylib
0x17d4000 - 0x17dffff libswiftCoreGraphics.dylib armv7 <95229d09c03d3eba9fbb038741503af3> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftCoreGraphics.dylib
0x17f4000 - 0x17fbfff libswiftCoreImage.dylib armv7 <59ca6e9173993aa39882799efdafd355> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftCoreImage.dylib
0x1808000 - 0x180ffff libswiftDarwin.dylib armv7 <764c0e157b49314088a4c9f8e1390a1a> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftDarwin.dylib
0x1820000 - 0x1823fff libswiftDispatch.dylib armv7 <e167a8d29dfd3694b0db2d3fc60adbb0> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftDispatch.dylib
0x1830000 - 0x185ffff libswiftFoundation.dylib armv7 <dde3f834069c3e6a9421572c0d01bbe3> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftFoundation.dylib
0x1898000 - 0x189ffff libswiftObjectiveC.dylib armv7 <786938b80ba63395aa5da6935df0c02e> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftObjectiveC.dylib
0x18ac000 - 0x18affff libswiftSecurity.dylib armv7 <12a8743e1ad636ebaaac523d1d709341> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftSecurity.dylib
0x18b8000 - 0x18c3fff libswiftUIKit.dylib armv7 <a04c41396cb939e6b3368360bfaffe10> /var/mobile/Containers/Bundle/Application/63493C9C-7C1B-47CA-83D3-8CC068537B89/KKBOX.app/Frameworks/libswiftUIKit.dylib
0x1fe5c000 - 0x1fe7ffff dyld armv7 <35ecdca1a767375e95ffa0f2a78d76d0> /usr/lib/dyld
0x22663000 - 0x2267efff libJapaneseConverter.dylib armv7 <1531c07cd9613bba8fc6fe9217f33612> /System/Library/CoreServices/Encodings/libJapaneseConverter.dylib
0x228f0000 - 0x22a5dfff AVFoundation armv7 <d269609e868231debb09b8b9b65a7367> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
...
这一段资料还可以提供我们另外一种资讯。
来看看这个crash report,KKBOX 直接放在/Users/USER 目录下,并没有放在sandbox 环境中,而明明就该载入armv7 的library,却载入了一堆armv6 的library,再仔细看看,出现了CydiaSubstrate 这些东西。
显然用户做了JB。
0x7d000 - 0x71cfff +KKBOX armv7 <f8a0ae9e6da833e0a9bd332e3d4b8a4c> /Users/USER/KKBOX.app/KKBOX
0x8b1000 - 0x8b1fff MobileSubstrate.dylib armv6 <ad3e6cb9e915360ebc71ccbf27bc4ea7> /Library/MobileSubstrate/MobileSubstrate.dylib
0x903000 - 0x905fff SubstrateLoader.dylib armv6 <974e4b1ab6e6397db859d79f37b7ab37> /Library/Frameworks/CydiaSubstrate.framework/Libraries/SubstrateLoader.dylib
0x929000 - 0x935fff Activator.dylib armv7s <24b72822b56d30f4b88c83448f3db656> /Library/MobileSubstrate/DynamicLibraries/Activator.dylib
0x972000 - 0x974fff Emphasize.dylib armv7 <4a31e195474039db8a69730adc9b7642> /Library/MobileSubstrate/DynamicLibraries/Emphasize.dylib
0x977000 - 0x97cfff CydiaSubstrate armv6 <abeb3e46b03b3abeb9d3feba7fefe2fb> /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate
0xa82000 - 0xa89fff libapplist.dylib armv7s <3ef6ef0d6b7d350982114b6b4221ef01> /usr/lib/libapplist.dylib
0xa90000 - 0xa95fff LocalIAPStore.dylib armv7s <f3341a7878d9319cb0bef7c2d2cbd896> /Library/MobileSubstrate/DynamicLibraries/LocalIAPStore.dylib
0xa9f000 - 0xaa5fff RePower.dylib armv7s <dce8d29ea843350baad4f700b0443795> /Library/MobileSubstrate/DynamicLibraries/RePower.dylib
0xaaa000 - 0xaaafff ToneEnabler.dylib armv7s <21fc136886163804b54e0524f7d1ca25> /Library/MobileSubstrate/DynamicLibraries/ToneEnabler.dylib
0xaad000 - 0xaaefff WeeLoader.dylib armv7 <7ff8f9166f93382eb27aba388d599a0a> /Library/MobileSubstrate/DynamicLibraries/WeeLoader.dylib
0xab1000 - 0xac2fff WinterBoard.dylib armv6 <2f7ede1815c93754b5c91a06195b485a> /Library/MobileSubstrate/DynamicLibraries/WinterBoard.dylib
0xad6000 - 0xadafff colorY0urBoardFree.dylib armv7s <2e3552b8fe5c3bb8967e25d652d5a33f> /Library/MobileSubstrate/DynamicLibraries/colorY0urBoardFree.dylib
0xadf000 - 0xae0fff zeppelin_uikit.dylib armv7 <1401eeb739bb3a5d88a45926eac68b46> /Library/MobileSubstrate/DynamicLibraries/zeppelin_uikit.dylib
0x77a5000 - 0x77b0fff QuickSpeak armv7s <9932eaf14fb23095b04ab610b05ba02e> /System/Library/AccessibilityBundles/QuickSpeak.bundle/QuickSpeak