[譯]《iOS Crash Dump Analysis》- 應(yīng)用程序中止崩潰

點贊評論,感覺有用的朋友可以關(guān)注筆者公眾號 iOS 成長指北,持續(xù)更新
原書為 iOS Crash Dump Analysis Book,已得作者授權(quán),歡迎 star

在本章中,我們將研究應(yīng)用程序中止崩潰。

通過崩潰報告異常類型來區(qū)分這類崩潰—— EXC_CRASH (SIGABRT) 。

我們關(guān)注這些從網(wǎng)上收集的大量崩潰信息。

一般原則

許多操作系統(tǒng)語言的支持模塊和庫都有用于檢測致命程序錯誤的代碼。一旦觸發(fā),操作系統(tǒng)將終止該應(yīng)用程序,這會導(dǎo)致 SIGABRT 崩潰。

SIGABRT 并沒有特別的原因。所以我們將查看各種示例,以便我們可以分析它們出現(xiàn)的各種情況。

有時,這種崩潰會在崩潰報告的 Application Specific Information 區(qū)域中提供一些信息。如果這不能揭示我們所需的詳細(xì)信息,但通??梢哉业揭l(fā)崩潰的模塊,并對代碼進(jìn)行逆向工程以了解具體是什么癥狀。

Kindle Create 崩潰

Kindle Create是一款應(yīng)用程序,作者可以利用手稿(比如 docx 文件)創(chuàng)建電子書。它通過 QuartzCore 庫進(jìn)行大量的繪制。

在發(fā)布預(yù)覽時,它發(fā)生崩潰并產(chǎn)生以下崩潰報告,為便于演示,將其截斷:

Process:               Kindle Create [3010]
Path:                  /Applications/Kindle Create.app/
Contents/MacOS/Kindle Create
Identifier:            com.amazon.kc
Version:               1.10 (1.10)
Code Type:             X86 (Native)
Parent Process:        ??? [1]
Responsible:           Kindle Create [3010]
User ID:               501

Crashed Thread:        16  Dispatch queue:
 com.apple.root.default-qos

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Application Specific Information:
abort() called

Application Specific Signatures:
Graphics kernel error: 0xfffffffb

Thread 16 Crashed:: Dispatch queue: com.apple.root.default-qos
0   libsystem_kernel.dylib          0xa73e7ed6
__pthread_kill + 10
1   libsystem_pthread.dylib         0xa75a0427
pthread_kill + 363
2   libsystem_c.dylib               0xa7336956
abort + 133
3   libGPUSupportMercury.dylib      0xa2aa342d
 gpusGenerateCrashLog + 160
4   com.apple.AMDRadeonX4000GLDriver    0x180cbb00
 gpusKillClientExt + 23
5   libGPUSupportMercury.dylib      0xa2aa4857
 gpusSubmitDataBuffers + 157
6   com.apple.AMDRadeonX4000GLDriver    0x180a293c
 glrATI_Hwl_SubmitPacketsWithToken + 143
7   com.apple.AMDRadeonX4000GLDriver    0x180fd9b0
 glrFlushContextToken + 68
8   libGPUSupportMercury.dylib      0xa2aa88c8
 gldFlushContext + 24
9   GLEngine                        0x9b416f5b
 glFlushRender_Exec + 37
10  com.apple.QuartzCore            0x9c1c8412
 CA::(anonymous namespace)::IOSurface::detach() + 166
11  com.apple.QuartzCore            0x9c1c7631
 CAOpenGLLayerDraw(CAOpenGLLayer*, double, CVTimeStamp const*,
    unsigned int) + 1988
12  com.apple.QuartzCore            0x9c1c6c9a
 -[CAOpenGLLayer _display] + 618
13  com.apple.QuartzCore            0x9c179f62
 -[CALayer display] + 158
14  com.apple.AppKit                0x916106ac
 -[NSOpenGLLayer display] + 305
15  com.apple.QuartzCore            0x9c1c9f77
 display_callback(void*, void*) + 59
16  com.apple.QuartzCore            0x9c1c9efa
 CA::DispatchGroup::dispatch(bool) + 88
17  com.apple.QuartzCore            0x9c1c9e9a
 CA::DispatchGroup::callback_0(void*) + 16
18  libdispatch.dylib               0xa72565dd
 _dispatch_client_callout + 50
19  libdispatch.dylib               0xa7263679
 _dispatch_queue_override_invoke + 779
20  libdispatch.dylib               0xa725818b
 _dispatch_root_queue_drain + 660
21  libdispatch.dylib               0xa7257ea5
 _dispatch_worker_thread3 + 100
22  libsystem_pthread.dylib         0xa759cfa5
 _pthread_wqthread + 1356
23  libsystem_pthread.dylib         0xa759ca32
 start_wqthread + 34

Thread 16 crashed with X86 Thread State (32-bit):
  eax: 0x00000000  ebx: 0xb0a79000  ecx: 0xb0a78acc
    edx: 0x00000000
  edi: 0xa75a02ca  esi: 0x0000002d  ebp: 0xb0a78af8
    esp: 0xb0a78acc
   ss: 0x00000023  efl: 0x00000206  eip: 0xa73e7ed6
      cs: 0x0000000b
   ds: 0x00000023   es: 0x00000023   fs: 0x00000023
      gs: 0x0000000f
  cr2: 0xa9847340

Logical CPU:     0
Error Code:      0x00080148
Trap Number:     132

Binary Images:

0x18099000 - 0x1815efff  com.apple.AMDRadeonX4000GLDriver
 (1.68.20 - 1.6.8)
 <DF3BB959-0C0A-3B6C-8E07-11B332128555>
  /System/Library/Extensions/AMDRadeonX4000GLDriver.bundle/
  Contents/MacOS/AMDRadeonX4000GLDriver

0xa2aa2000 - 0xa2aacfff  libGPUSupportMercury.dylib
 (16.7.4)
 <C71E29CF-D4C5-391D-8B7B-739FB0536387>
  /System/Library/PrivateFrameworks/GPUSupport.framework/
  Versions/A/Libraries/libGPUSupportMercury.dylib

從堆棧回溯中可以看到 OpenGL 管道已刷新。
這導(dǎo)致 com.apple.AMDRadeonX4000GLDriver 檢測到命令問題并觸發(fā)崩潰。 我們看到該代碼為崩潰報告提供了自定義信息。

3   libGPUSupportMercury.dylib      0xa2aa342d
 gpusGenerateCrashLog + 160

我們可以在這里使用 Hopper 脫殼和逆向工程工具。

通過在我們的Mac上找到二進(jìn)制文件,我們可以要求Hopper不僅分解有問題的代碼,而且還生成偽代碼。對于大多數(shù)開發(fā)人員來說,很難立即了解匯編代碼,因為當(dāng)今很少使用匯編代碼。這就是偽代碼最有價值的原因。

我們首先從崩潰報告的 Binary Images 部分找到二進(jìn)制文件的位置。

/System/Library/PrivateFrameworks/GPUSupport.framework/
Versions/A/Libraries/libGPUSupportMercury.dylib

對于系統(tǒng)二進(jìn)制文件而言,遍歷文件層次結(jié)構(gòu)可能會很麻煩,因為它們深深地嵌套在文件系統(tǒng)中。

如果 Hopper 已經(jīng)正在運(yùn)行,那么快速選擇正確文件的方法是使用命令行。

'/Applications/Hopper Disassembler v4.app/Contents/
MacOS/hopper' -e /System/Library/PrivateFrameworks/
GPUSupport.framework/Versions/A/Libraries/
libGPUSupportMercury.dylib

如果Hopper沒有運(yùn)行,我們可以啟動它。我們可以啟動 Finder 程序并選擇 'Go To Folder' 以選擇文件夾

/System/Library/PrivateFrameworks/GPUSupport.framework/
Versions/A/Libraries/
finder_support_mercury.png

然后,我們可以簡單地將libGPUSupportMercury.dylib從 Finder 中拖到 Hopper 應(yīng)用的主面板中,它將開始處理文件。

drag_file_to_hopper.png

我們需要選擇要脫殼的體系結(jié)構(gòu)。 它必須與我們正在診斷的內(nèi)容匹配。從崩潰報告中我們可以看到它是 Code Type X86 (Native),這意味著我們需要在 Hopper 中選擇32位體系結(jié)構(gòu)選項。

hopper_32bit.png

然后我們點擊 Next,然后點擊 OK

片刻之后,文件將被處理。 然后我們可以選擇 Navigate -> Go To Address or Symbol 并提供地址 _gpusGenerateCrashLog 。注意,下劃線是在前面的。 C 編譯器會在生成目標(biāo)文件之前自動將其放入。 在過去,這樣做是為了使手寫的匯編代碼在鏈接期間不會與C 語言符號沖突。

在默認(rèn)視圖中,Hopper 將顯示該功能的反匯編代碼。

hopper_diss.png

通過選擇偽代碼按鈕(用紅色圓圈顯示),我們可以使 Hopper 對該功能生成更容易理解的描述。

hopper_pseudocode.png

這是 Hopper 的輸出:

int _gpusGenerateCrashLog(int arg0, int arg1, int arg2) {
 rdi = arg0;
 r14 = arg2;
 rbx = arg1;
 if (*0xc678 != 0x0) {
   rax = *___stack_chk_guard;
   if (rax != *___stack_chk_guard) {
     rax = __stack_chk_fail();
   }  
 }
 else {
   if (rdi != 0x0) {
     IOAccelDeviceGetName(*(rdi + 0x230), 0x0, 0x14);
   }  
   if ((rbx & 0x20000000) == 0x0) {
     rdx =
     "Graphics kernel error: 0x%08x\n";
   }  
   else {
     rdx =
  "Graphics hardware encountered an error and was reset:
   0x%08x\n";
   }  
   sprintf_l(var_A0, 0x0, rdx);
   *0xc680 = var_A0;
   rax = abort();
 }
 return rax;
}

在這里,我們可以看到兩種報文。一種是 :

"Graphics kernel error: 0x%08x\n"

另一種是:

"Graphics hardware encountered an error and was reset: 0x%08x\n"

實際上,我們在崩潰報告中看到以下內(nèi)容:

Application Specific Signatures:
Graphics kernel error: 0xfffffffb

不幸的是,尚不清楚此錯誤是什么意思。 我們需要應(yīng)用程序的作者打開OpenGL命令級日志記錄,以便了解圖形驅(qū)動程序拒絕了哪些繪圖命令。

通過使用不同的 Mac 和顯卡配置將會是實驗變得很有趣。讓我們判斷這是一個是特定的驅(qū)動問題,或一個通用的 OpenGL 問題。

類型混淆

編譯器在靜態(tài)類型檢查方面做得非常出色。但在動態(tài)推斷類型時,可能會出現(xiàn)問題。而 配置文件在這方面尤為麻煩。 很容易為配置參數(shù)設(shè)置錯誤的類型

我們借助示例代碼icdab_nsdata來說明我們的觀點。

從配置文件中獲取 NSData 對象

思考以下示例代碼

- (BOOL)application:(UIApplication *)application
 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application
    // launch.

    NSData *myToken = [[NSData alloc] initWithData:
    [[NSUserDefaults standardUserDefaults]
     objectForKey:@"SomeKey"]];

    NSLog(@"My data is %@ - ok since we can handle a nil",
     myToken);

    id stringProperty = @"Some string";
    NSData *problemToken = [[NSData alloc]
     initWithData:stringProperty];

    NSLog(@"My data is %@ - we have probably crashed by now",
     problemToken);
    return YES;
}

這段代碼試圖做兩件事。 首先,它嘗試從其配置中獲取 token。我們假設(shè)在之前的運(yùn)行中,用戶已經(jīng)以 SomeKey為鍵值保存了一個 NSData 格式的 token

按照設(shè)計, NSData 對象可以處理所提供的數(shù)據(jù)是否為 nil 的情況。 因此,如果尚未保存數(shù)據(jù),代碼仍可正常運(yùn)行。

token可能只是一個簡單的十六進(jìn)制字符串,例如 7893883873a705aec69e2942901f20d7b1e28dec

上面的代碼中有一個字符串 stringProperty,用于模擬以下情況:用戶存存儲的 token 是一個字符串而不是 NSData對象。
可能是它被手動復(fù)制并粘貼到用戶的 plist 文件中。 如果 initWithData 方法參數(shù)為 NSString,那么就無法創(chuàng)建 NSData 對象。 然后發(fā)生了崩潰。

如果我們運(yùn)行該代碼,我們將得到以下崩潰報告,為了便于演示,將其截斷:

反序列化崩潰報告

Incident Identifier: 12F72C5C-E9BD-495F-A017-832E3BBF285E
CrashReporter Key:   56ec2b40764a1453466998785343f1e51c8b3849
Hardware Model:      iPod5,1
Process:             icdab_nsdata [324]
Path:                /private/var/containers/Bundle/Application/
98F79023-562D-4A76-BC72-5E56D378AD98/
icdab_nsdata.app/icdab_nsdata
Identifier:          www.perivalebluebell.icdab-nsdata
Version:             1 (1.0)
Code Type:           ARM (Native)
Parent Process:      launchd [1]
Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Filtered syslog:
None found

Last Exception Backtrace:
0   CoreFoundation                  0x25aa3916
 __exceptionPreprocess + 122
1   libobjc.A.dylib                 0x2523ee12
 objc_exception_throw + 33
2   CoreFoundation                  0x25aa92b0
-[NSObject+ 1045168 (NSObject) doesNotRecognizeSelector:]
 + 183
3   CoreFoundation                  0x25aa6edc
 ___forwarding___ + 695
4   CoreFoundation                  0x259d2234
 _CF_forwarding_prep_0 + 19
5   Foundation                      0x2627e9a0
-[_NSPlaceholderData initWithData:] + 123
6   icdab_nsdata                    0x000f89ba
-[AppDelegate application:
didFinishLaunchingWithOptions:] + 27066 (AppDelegate.m:26)
7   UIKit                           0x2a093780
 -[UIApplication _handleDelegateCallbacksWithOptions:
 isSuspended:restoreState:] + 387
8   UIKit                           0x2a2bb2cc
 -[UIApplication _callInitializationDelegatesForMainScene:
 transitionContext:] + 3075
9   UIKit                           0x2a2bf280
-[UIApplication _runWithMainScene:transitionContext:
completion:] + 1583
10  UIKit                           0x2a2d3838
__84-[UIApplication _handleApplicationActivationWithScene:
transitionContext:completion:]_block_invoke3286 + 31
11  UIKit                           0x2a2bc7ae
 -[UIApplication workspaceDidEndTransaction:] + 129
12  FrontBoardServices              0x27146c02
 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 13
13  FrontBoardServices              0x27146ab4
-[FBSSerialQueue _performNext] + 219
14  FrontBoardServices              0x27146db4
-[FBSSerialQueue _performNextFromRunLoopSource] + 43
15  CoreFoundation                  0x25a65dfa
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
 + 9
16  CoreFoundation                  0x25a659e8
__CFRunLoopDoSources0 + 447
17  CoreFoundation                  0x25a63d56
 __CFRunLoopRun + 789
18  CoreFoundation                  0x259b3224
 CFRunLoopRunSpecific + 515
19  CoreFoundation                  0x259b3010
CFRunLoopRunInMode + 103
20  UIKit                           0x2a08cc38
 -[UIApplication _run] + 519
21  UIKit                           0x2a087184
 UIApplicationMain + 139
22  icdab_nsdata                    0x000f8830
 main + 26672 (main.m:14)
23  libdyld.dylib                   0x2565b86e tlv_get_addr
 + 41

不幸的是,這種崩潰沒有將更多的有用信息(例如“特定于應(yīng)用程序的信息”)注入到崩潰報告中。

但是,我們確實會在系統(tǒng)日志(應(yīng)用程序的控制臺日志)中獲取信息:

default 13:36:58.000000 +0000   icdab_nsdata     
My data is <> - ok since we can handle a nil

default 13:36:58.000000 +0100   icdab_nsdata     
-[__NSCFConstantString _isDispatchData]:
unrecognized selector sent to instance 0x3f054

default 13:36:58.000000 +0100   icdab_nsdata
     *** Terminating app due to uncaught exception
    'NSInvalidArgumentException', reason:
    '-[__NSCFConstantString _isDispatchData]:
     unrecognized selector sent to instance 0x3f054'

    *** First throw call stack:
    (0x25aa391b 0x2523ee17 0x25aa92b5 0x25aa6ee1 0x259d2238
     0x2627e9a5 0x3d997
     0x2a093785 0x2a2bb2d1 0x2a2bf285 0x2a2d383d 0x2a2bc7b3
      0x27146c07
      0x27146ab9 0x27146db9 0x25a65dff 0x25a659ed 0x25a63d5b
       0x259b3229
      0x259b3015 0x2a08cc3d 0x2a087189 0x3d80d 0x2565b873)

default 13:36:58.000000 +0100
    SpringBoard Application
  'UIKitApplication:www.perivalebluebell.icdab-nsdata[0x51b9]'
    crashed.

default 13:36:58.000000 +0100
UserEventAgent
     2769630555571: id=www.perivalebluebell.icdab-nsdata
   pid=386, state=0

default 13:36:58.000000 +0000   ReportCrash
     Formulating report for corpse[386] icdab_nsdata

default 13:36:58.000000 +0000   ReportCrash
     Saved type '109(109_icdab_nsdata)'
    report (2 of max 25) at
    /var/mobile/Library/Logs/CrashReporter/
    icdab_nsdata-2018-07-27-133658.ips

從這里我們可以看到問題是 __NSCFConstantString 無法響應(yīng) _isDispatchData 是因為 NSString 不是數(shù)據(jù)所提供的對象。

Apple SDK 具有私有實現(xiàn)類,以支持我們使用的公共對象。 錯誤報告將引用這些私有類。 因此,他們的名字可能不再令人熟悉。

可以通過一種簡單的方法進(jìn)行管理,并找出具體的表示映射到要搜索的類類型定義的哪個對象。

方便的是,其他工程師已經(jīng)在框架上使用 class-dump 工具來生成所有 Objective-C 的類定義,并將它們存儲在 GitHub上。他們使用 class-dump工具。這使得 Objective-C 的所有私有框架符號都很容易搜索到。

我們可以找到關(guān)于 _isDispatchData的定義。

/* Generated by RuntimeBrowser
   Image: /System/Library/Frameworks/Foundation.framework/
   Foundation
 */

@interface _NSDispatchData : NSData

+ (bool)supportsSecureCoding;

- (bool)_allowsDirectEncoding;
- (id)_createDispatchData;
- (bool)_isDispatchData;
- (Class)classForCoder;
- (id)copyWithZone:(struct _NSZone { }*)arg1;
- (void)encodeWithCoder:(id)arg1;
- (void)enumerateByteRangesUsingBlock:(id /* block */)arg1;
- (void)getBytes:(void*)arg1;
- (void)getBytes:(void*)arg1 length:(unsigned long long)arg2;
- (void)getBytes:(void*)arg1 range:(struct _NSRange
   { unsigned long long x1; unsigned long long x2; })arg2;
- (unsigned long long)hash;
- (id)initWithCoder:(id)arg1;
- (id)subdataWithRange:(struct _NSRange
  { unsigned long long x1; unsigned long long x2; })arg1;

@end

同樣,我們可以查找 __NSCFConstantString。

/* Generated by RuntimeBrowser
   Image: /System/Library/Frameworks/CoreFoundation.framework/
   CoreFoundation
 */

@interface __NSCFConstantString : __NSCFString

- (id)autorelease;
- (id)copyWithZone:(struct _NSZone { }*)arg1;
- (bool)isNSCFConstantString__;
- (oneway void)release;
- (id)retain;
- (unsigned long long)retainCount;

@end
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 夜鶯2517閱讀 128,214評論 1 9
  • 版本:ios 1.2.1 亮點: 1.app角標(biāo)可以實時更新天氣溫度或選擇空氣質(zhì)量,建議處女座就不要選了,不然老想...
    我就是沉沉閱讀 7,510評論 1 6
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,896評論 28 54
  • 兔子雖然是枚小碩 但學(xué)校的碩士四人寢不夠 就被分到了博士樓里 兩人一間 在學(xué)校的最西邊 靠山 兔子的室友身體不好 ...
    待業(yè)的兔子閱讀 2,787評論 2 9

友情鏈接更多精彩內(nèi)容