歡迎光臨
每天分享高質量文章

手把手教你查看和分析iOS的crash崩潰異常

作者:歐陽大哥2013

鏈接:https://www.jianshu.com/p/cf0945f9c1f8


要學會看crash崩潰和報告


一個應用程式並不總會一直運行的很好,它總會有出現crash崩潰的情況。如果在應用程式中接入了一些第三方的crash收集工具或者自建crash收集報告平臺的話將會很好的幫助開發者去分析和解決應用程式在線上運行的問題,當出現的崩潰問題能得到及時的解決和快速的修複時必將會大大的提升應用程式的用戶體驗。


當前比較流行的crash收集分析工具很多都是基於開源的KSCrash代碼來進行封裝和改進的。蘋果自身也構建了一套crash採集和分析的機制,你可以從真機的聯機日誌或者從開發者賬號中去查看對應的crash信息。網絡上也有很多關於crash分析的文章,以及crash堆棧符號化處理的文章。這裡假定你已經瞭解了一些查看crash報告的方法和技巧以及一些簡單的crash分析技巧,因為這些是作為開發者需要具備的技能之一。


一個objc_msgSend+16崩潰棧


應用程式出現的crash崩潰異常有一些能夠簡單的被分析和解決,往往這些crash崩潰異常都會帶有明確的背景關係信息和函式呼叫層級堆棧。但並不是所有的crash崩潰異常都能被簡單的解決,尤其是那些沒有明確背景關係信息的函式呼叫堆棧或者那些呼叫堆棧中沒有一個函式或者方法能夠被直接定位到原始碼的場景,就如下麵這個崩潰的函式呼叫棧(部分信息):


Incident Identifier: 85BE3461-D7FD-4043-A4B9-1C0D9A33F63D
CrashReporter Key:   9ec5a1d3b8d5190024476c7068faa58d8db0371f
Hardware Model:      iPhone7,2
Code Type:       ARM-64
Parent Process:  ? [1]
Date/Time:       2018-08-06 16:36:58.000 +0800
OS Version:      iOS 10.3.3 (14G60)
Report Version:  104

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Codes: 0x00000000 at 0x00000005710bbeb8
Crashed Thread:  2

Thread 2 name:  WebThread
 Thread 2 Crashed:
0   libobjc.A.dylib                 objc_msgSend + 16
1   UIKit                           -[UIWebDocumentView _updateSubviewCaches] + 40
2   UIKit                           -[UIWebDocumentView subviews] + 92
3   UIKit                           -[UIView(CALayerDelegate) _wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:] + 72
4   UIKit                           -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1256
5   QuartzCore                      -[CALayer layoutSublayers] + 148
6   QuartzCore                      CA::Layer::layout_if_needed(CA::Transaction*) + 292
7   QuartzCore                      CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32
8   QuartzCore                      CA::Context::commit_transaction(CA::Transaction*) + 252
9   QuartzCore                      CA::Transaction::commit() + 504
10  QuartzCore                      CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 120
11  CoreFoundation                  __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
12  CoreFoundation                  __CFRunLoopDoObservers + 372
13  CoreFoundation                  CFRunLoopRunSpecific + 456
14  WebCore                         RunWebThread(void*) + 456
15  libsystem_pthread.dylib         _pthread_body + 240
16  libsystem_pthread.dylib         _pthread_body + 0

Thread 2 crashed with ARM-64 Thread State:
  cpsr: 0x0000000020000000     fp: 0x000000016e18d7c0     lr: 0x000000018e2765fc     pc: 0x0000000186990150 
    sp: 0x000000016e18d7b0     x0: 0x0000000174859740     x1: 0x000000018eb89b7b    x10: 0x0000000102ffc000 
   x11: 0x00000198000003ff    x12: 0x0000000102ffc290    x13: 0xbadd8a65710bbead    x14: 0x0000000000000000 
   x15: 0x000000018caeb48c    x16: 0x00000005710bbea8    x17: 0x000000018e2765d4    x18: 0x0000000000000000 
   x19: 0x0000000103a52800     x2: 0x0000000000000000    x20: 0x00000000000002a0    x21: 0x0000000000000000 
   x22: 0x0000000000000000    x23: 0x0000000000000000    x24: 0x0000000000000098    x25: 0x0000000000000000 
   x26: 0x000000018ebade52    x27: 0x00000001ad018624    x28: 0x0000000000000000    x29: 0x000000016e18d7c0 
    x3: 0x000000017463db60     x4: 0x0000000000000000     x5: 0x0000000000000000     x6: 0x0000000000000000 
    x7: 0x0000000000000000     x8: 0x00000001acfb9000     x9: 0x000000018ebf8829 

Binary Images:
       0x100030000 -        0x1022cbfff +xxxx arm64  <6b98f446542b3de5818256a8f2dc9ebf> /var/containers/Bundle/Application/441619EF-BD56-4738-B6CF-854492CDFAC9/xxxx.app/xxxx
       0x1063f8000 -        0x106507fff  MacinTalk arm64  <0890ce05452130bb9af06c0a04633cbb> /System/Library/TTSPlugins/MacinTalk.speechbundle/MacinTalk
       0x107000000 -        0x1072e3fff  TTSSpeechBundle arm64   /System/Library/TTSPlugins/TTSSpeechBundle.speechbundle/TTSSpeechBundle
...
       0x18e03d000 -        0x18ede3fff  UIKit arm64  <314063bdf85f321d88d6e24a0de464a2> /System/Library/Frameworks/UIKit.framework/UIKit
       0x18ede4000 -        0x18ee0cfff  CoreBluetooth arm64   /System/Library/Frameworks/CoreBluetooth.framework/CoreBluetooth

這是一個在iOS10.3.3版本的64位設備上的一條crash異常報告的片段信息,要記住這些信息,它對定位crash崩潰異常有很大的幫助。從崩潰的函式呼叫棧中可以看出異常是出現在最頂層的函式呼叫objc_msgSend+16處,也就是在objc_msgSend函式的第5條指令處(通常情況下arm體系結構中每條指令占用4個位元組,上述的信息表明是崩潰在函式的第16個位元組的偏移地址處,也就是函式的第5條指令處)。崩潰異常型別顯示為EXC_BAD_ACCESS表明是產生了無效的地址的讀寫訪問,整個崩潰函式呼叫棧中沒應用程式中的任何背景關係信息。objc_msgSend函式是runtime方法執行的核心引擎而且呼叫如此的頻繁,函式內部是不可能有BUG的。 那麼為什麼會崩潰在這呢?


當異常出現在沒有原始碼的函式內部時,唯一的方法就是去看它內部的“原始碼”實現


既然出現問題是在objc_msgSend函式的第5條指令處,可以來看看這個函式實現的彙編代碼指令開頭片段:


;iOS10以後的objc_msgSend的部分實現代碼。
_objc_msgSend:
00000001800bc1400>    cmp     x0, #0x0     ;判斷物件receiver和0進行比較
00000001800bc1444>    b.le    0x1800bc1ac    ;如果物件指標為0或者高位為1則執行特殊處理跳轉。
00000001800bc1488>    ldr     x13, [x0]           ;取出物件的isa指標賦值給x13
00000001800bc14c12>   and x16, x13, #0xffffffff8   ;得到物件的Class物件指標賦值給x16
00000001800bc15016>   ldp x10, x11, [x16, #0x10]    ;取出Class物件的cache成員分別儲存到x10,x11暫存器中 
-----------------------------------------------------------上面的指令就是代碼崩潰處。
00000001800bc15420>   and     w12, w1, w11

無論是真機還是模擬器,XCODE都支持在運行時來查看任何呼叫的函式的彙編代碼實現,你可以通過設置符號斷點或者進入彙編除錯樣式以及單指令跳轉的方式來查看函式的彙編代碼實現。


從代碼中可以看出是在讀取物件的Class物件指標的資料成員cache時出現了無效的地址訪問異常。但是物件的Class物件這部分定義資料是儲存在行程記憶體的資料區段中,並且伴隨著整個應用的生命周期而存在,是不可能被釋放和銷毀的,因此正常情況下是不可能存在非法記憶體地址訪問異常的。會出現這種問題的原因就是呼叫方法的OC物件被銷毀了,再說具體一點就是對一個已經被釋放掉的OC物件繼續呼叫了實體方法而導致的。因此當出現這種型別的崩潰時,不管是否有明確背景關係,其原因都是一致的。下麵這張圖就能很清楚的說明其中的原因了:


物件被銷毀前後記憶體佈局對比圖


實際上在arm64位系統中isa中儲存的並不是物件的Class物件地址,上面的圖目的是為了更加直觀的顯示問題原因。


一個OC物件obj在被銷毀前,其中的isa指標會指向正確的Class物件所在的記憶體地址。因此呼叫objc_msgSend方法將會正常的運行,而一旦obj物件被銷毀後,為其分配的堆記憶體將被回收用作其他用途,因此有可能這部分記憶體區域的資料會被覆寫。當對一個已經釋放了的OC物件繼續呼叫實體方法時,在objc_msgSend函式內部讀取到obj的isa指標得到的將是一個未知或者有可能無效的指標值。所以當對這個未知地址指向的記憶體進行訪問時就出現了上面的EXC_BAD_ACCESS的異常崩潰了。


CPU指令中操作暫存器和常數的指令一般不會產生崩潰異常,比如上面的第1,2,4,6條指令;而一般產生訪問異常的指令是發生在那些訪問記憶體地址的指令當中,比如第3條和5條。


也許你會好奇既然obj物件已經被釋放了,為什麼崩潰會出現在objc_msgSend函式的第5條指令,其中的第3條指令是訪問物件的isa資料的,為什麼不崩潰在這呢? 其實答案很簡單,因為幾乎所有的OC物件都是從堆記憶體區域中分配記憶體的,所以當某個OC物件被銷毀後,其所占用的記憶體仍然會放回堆記憶體區域中進行管理,而堆記憶體區域的地址是可以進行任意的讀寫訪問的,所以即使物件被銷毀釋放,仍然是可以訪問物件所指向的記憶體區域的資料的。


應用程式出現崩潰異常時除了函式呼叫棧可提供分析參考外,還可以從暫存器中的值來進行一步分析。根據上述的函式指令實現中可以看出:


x0  暫存器中的儲存的就是那個被銷毀了的物件指標。
x1  暫存器中儲存的就是產生崩潰的物件的方法名稱的地址。
x13 暫存器中儲存的就是物件的isa指標值。
x16 暫存器中儲存的就是物件的Class指標物件。


函式崩潰處指令為:


ldp x10, x11, [x16, #0x10] 

這時候因為x16中其實儲存的是一個非法的Class物件指標地址了,所以當執行ldp指令來從x16所指向地址的偏移0x10處讀取記憶體資料時就產生了崩潰,而崩潰的異常代碼:


  Exception Codes: 0x00000000 at 0x00000005710bbeb8

中的地址值也剛好和x16暫存器中的值是一致的。也就是表明x16中所儲存的Class物件指標就是一個非法和無效的記憶體地址。


在所有的OC方法中如果你設置了符號斷點那麼在方法開始執行時x0中儲存的總是執行方法的物件,也是第一個方法的引數;x1中總是儲存的執行的方法的名稱字串,也是第二個方法的引數;然後x2到x15有可能依次是方法的其他引數。因此通常情況下你可以在除錯控制臺中輸入: po $x0 來顯示物件信息, p (char*)$x1 來顯示方法名稱。 具體的詳細介紹可以參考我的另外一篇文章:暫存器介紹


上面的崩潰呼叫棧中,所有的函式和方法都是系統函式並沒有程式自身的原始碼,因此很難跟蹤或者發現問題產生的原因,因為此時是無法知道是哪個類的物件執行方法呼叫而產生的crash了,唯一的線索就是x1暫存器中的值了。這個暫存器中的值儲存的是呼叫的方法名, 它是一個SEL型別的資料,因此可以根據x1中儲存的方法名來進行反推,也就是從方法名來反推出產生崩潰的物件的類名。


x1暫存器中儲存的方法的記憶體地址是存在於某個加載的庫Image的代碼段中,因此可以在崩潰日誌的Binary Images串列中找到定義方法名的庫Image信息,Binary Images串列中的每個庫Image都有這個庫加載的開始和結束地址以及路徑名稱,可以很容易就從這些區間串列中找到x1暫存器所指的方法名到底屬於哪個庫。就上面的例子來說可以很明確的看到方法地址0x18eb89b7b是屬於:


 0x18e03d000 -  0x18ede3fff  UIKit arm64  <314063bdf85f321d88d6e24a0de464a2> /System/Library/Frameworks/UIKit.framework/UIKit


也就是UIKit庫中定義的某個物件在執行x1所指的方法而產生了崩潰。有了這個更進一步的信息後就可以在原始碼中進行檢查看看哪部分代碼呼叫到了產生崩潰的庫中所定義的物件了(當然UIKit這裡不具備代表性,實際中崩潰時方法名也許會在其他的庫中)。這樣就從一定程度上能夠縮小排查問題的範圍。


常見的崩潰異常分析定位方法


當出現了沒有背景關係的崩潰異常呼叫棧時,並不是對它束手無策。除了可以根據異常型別(signal的型別)分析外,還可以借助搜索引擎以及一些常見的問題解答站點來尋找答案,當然還可以借助下麵列出幾種定位和分析的方法:


1、開原始碼法

這個方法其實很簡單,蘋果其實開源了非常多的基礎庫的原始碼,因此當程式崩潰在這些開源的基礎庫上時就可以去下載對應的基礎庫的原始碼進行閱讀。然後從原始碼上進行問題的分析,從而找到產生異常崩潰的原因。你可以從https://opensource.apple.com處去下載開源的最新的原始碼。這種方法的缺點是並不是所有的代碼都是開源的,而且開源的代碼並不一定是你真機設備上運行的iOS版本。因此這種方法只能是一種輔助方法。


2、方法符號斷點法

採用這種方法時,確保你手頭上要有一臺和產生崩潰異常問題的操作系統版本相同的真機設備,以方便聯機除錯和運行。你可以在崩潰異常報告的:


OS Version:      iOS 10.3.3 (14G60)

部分看到產生異常的操作系統版本號,就如本文的例子裡面產生異常的操作系統版本號為iOS 10.3.3。因為相同的操作系統版本號中所有庫中代碼實現的都是一樣的。如果實在沒有對應的版本號的設備則可以試圖找一臺版本號最相近的設備。明確了操作系統版本和真機設備後再從代碼倉庫中檢出和你線上相同版本的應用程式的原始碼(假如崩潰呼叫棧中沒有任何我們編寫的函式代碼則這個條件要求不必那麼嚴格)。並打開專案工程,然後為產生崩潰的函式呼叫棧的棧頂函式或者方法名添加一個符號斷點。如果你不知道如何添加符號斷點請參考文章:https://blog.csdn.net/xuhen/article/details/77747456, 或者查找關鍵字:“XCODE 符號斷點”。


設置符號斷點的方法或者函式名時可以有如下的選擇:


1、如果產生崩潰的棧頂是一個OC物件的方法則可以直接用這個類名和方法名來設置符號斷點。

2、如果產生崩潰的棧頂是一個通用的C函式比如objc_msgSend、free、objc_release則考慮用函式呼叫棧的第二層函式和方法名來設置符號斷點。比如文本例子中的-[UIWebDocumentView _updateSubviewCaches]方法。

3、如果產生崩潰的函式呼叫棧頂是一個沒有對外暴露的C函式,因為這種函式設置符號斷點的難度比交大,所以往往考慮採用函式呼叫棧的第二層函式或者方法名來做為符號斷點。


設置符號斷點的目的是為了在崩潰函式呼叫堆棧重現時,能在運行時的斷點處進行動態分析。當你設置了符號斷點後,如果程式邏輯運行到這個函式或者方法時,系統就會在設置的方法或者函式的第一條指令處停止下來。這時候就可以查看此時的函式呼叫棧是否和產生崩潰時的呼叫棧相符,如果相符合那麼表明能夠重現可能發生問題的邏輯了,如果斷點處的呼叫棧和產生崩潰的呼叫棧不相同,則可能需要讓程式繼續運行,以便下次在同樣斷點處時進行呼叫棧的比較,因為設置斷點的方法名並不一定只在一處被呼叫。


符號斷點的設置


當程式停在了設置符號斷點的函式或者方法的開始地址後,接下來就需要在這個方法內進行第二個斷點的設置,設置的地方就是崩潰函式呼叫棧中函式呼叫上層函式的偏移處,這個可以在崩潰的報告中看到:


0   libobjc.A.dylib                 objc_msgSend + 16
1   UIKit                          -[UIWebDocumentView _updateSubviewCaches] + 40


也就是需要在_updateSubviewCaches函式的第11條指令或者函式的第40個偏移位元組附近處添加一個斷點。這樣當程式運動到斷點處時就可以在函式呼叫上層函式前查看各暫存器的值從而進行問題的定位和分析。


運行到產生崩潰異常的指令


一般情況下崩潰函式棧報告中除棧頂函式外的每一層函式名後 + 的數字表明是在當前函式的對應的地址偏移處附近進行了上層函式的呼叫,也就是對應的地址偏移附近一般都會存在一條bl指令或者blr這兩條指令,這兩條指令的作用就是執行函式的呼叫。


通過二次斷點的設置,程式運行到斷點時的指令是:


0x18c0248fc 36>: bl     0x1893042dc   ;0x1893042dc 這個地址就是objc_msgSend的函式地址


本例子的異常崩潰的原因是對一個已經釋放的物件繼續呼叫方法而產生的崩潰。所以當斷點停在指令處時,我們可以在右下角的lldb控制臺中打印指令:


(lldb)po $x0
<__nsarraym class="hljs-number" style="font-size: inherit;line-height: inherit;color: rgb(174, 135, 250);word-wrap: inherit !important;word-break: inherit !important;">0x1c044c2a0>(
<UIWebOverflowScrollView0x1281d7e00; frame = (0 0375 603); clipsToBounds = YES; gestureRecognizers = <NSArray0x1c0851190>; layer = 0x1c4426ba0>; contentOffset: {00}; contentSize: {37512810}; adjustedContentInset: {0000}>
)

(lldb) p (char*)$x1
(char *) $6 = 0x000000018cb9dd70 "release"
(lldb)


可以看出x0是一個陣列物件,而x1中則是release方法。這樣就進一步明確了是對一個已經釋放了的陣列物件呼叫了release方法而導致異常崩潰了。至於x0是一個什麼陣列以及儲存在哪裡,則可以通過彙編指令中的x0暫存器的使用進行回溯往上查找指令來進一步分析了。其實這個問題如果進一步觀察就可以看出:崩潰的執行緒並不是出現在主執行緒,而是在一個工作執行緒中。而視圖的操作基本都應該放在主執行緒進行,因此當主執行緒的某些子視圖陣列物件被釋放後,這裡又在輔助執行緒中進行讀取訪問,就出現了上面的異常崩潰問題了。


在函式呼叫bl或者blr指令處設置斷點後,因為根據ABI規則所有非浮點數的引數分別依次儲存在x0,x1,….這些暫存器中。所以可以在斷點處分別打印出這些暫存器的值就可以知道函式呼叫前所傳遞的引數值了。這個方法非常有助於進行問題的定位和分析。


3、手動重現法

有時候即使你設置了符號斷點,場景依然無法重現,這時候就需要採用一些特殊的手段,那就是手動的執行方法呼叫。實現方式很簡單就是在某個演示代碼中人為的進行崩潰棧頂函式的呼叫。就比如上面的例子當[UIWebDocumentView _updateSubviewCaches]方法一直不被執行時,就可以自己手動的去創建一個UIWebDocumentView物件,並手動的呼叫對應的方法_updateSubviewCaches即可。這裡存在的兩個問題是有可能這個類並沒有對外進行宣告,或者我們並不知道方法的引數型別或者需要傳遞的值。對於第一個問題解決的方法可以採用NSClassFromString來得到類信息併進行物件創建。而第二個問題則可以借助一些工具比如class-dump或者一些其他的手段來確認方法的引數個數和引數型別。總之,目的就是為了能夠進入函式的斷點,甚至都可以在不知道如何傳遞引數時將所有的引數都傳值為0或者nil來臨時解決問題。下麵就是模擬崩潰函式的呼叫實現代碼:


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

    //因為類名和方法名都未對外公開,我們可以借助一些技術手段來讓某個特定的方法執行,目的是為了能夠進入到方法的內部實現。
    Class cls = NSClassFromString(@"UIWebDocumentView");
    id obj = [[cls alloc] init];
    SEL sel = sel_registerName("_updateSubviewCaches");
    [obj performSelector:sel];

   //...
}

測試代碼可以寫在任何一個地方,這裡為了方便就在程式啟動處加上測試代碼。等代碼編寫完畢後,就可以為方法設置符號斷點。這樣當程式一運行時就一定能夠進入到這個函式的內部去。一旦函式被執行後出現了斷點,就可以按照第2種方法中的介紹進行崩潰分析了。


其實第3種方法的原則就是只要能讓產生崩潰異常的方法被呼叫,這其中可以嘗試著採用各種手段將物件和方法run起來。


4、第三方工具靜態分析法

前面兩種介紹的都是動態分析法, 有時候還可以借助一些反編譯的工具來對程式代碼進行靜態分析。比如像Hopper或者IDA之類的工具。缺點就是這些工具是收費的,而且效果沒有動態分析那麼的好。在使用上個人覺得IDA分析工具更加友好和強大一些。


採用第三方工具時需要找到產生崩潰的函式所在的庫,函式所在的庫在崩潰的函式呼叫棧串列中就能找到了。如果崩潰函式是在應用程式本身中被定義,那麼需要將上傳到appstore的ipa檔案解壓縮並提取出其中的可執行程式用工具打開即可。如果崩潰函式是在某個系統庫中被定義,那麼可從如下的路徑:

~/Library/Developer/Xcode/iOS DeviceSupport/


iOS DeviceSupport這個檔案夾下的內容將展示你所有曾經聯機除錯過的各種操作系統版本的庫的一份拷貝,如果你沒有真機除錯過出現崩潰的操作系統版本,請找一個安裝了這個操作系統版本的真機設備,並聯機,這樣你的檔案夾中就會有對應的操作系統版本下的系統庫的拷貝信息了。

UIKit庫的路徑


中找到對應的產生崩潰的手機操作系統版本號的庫檔案:10.3.3(14G60)/Symbols/System/Library/Frameworks/UIKit.framework/UIKit


當用IDA工具打開對應的庫檔案或者可執行檔案時你看到的將是這個庫檔案的所有彙編形式的代碼和資料。因此你可以通過搜索選單來查找產生崩潰的函式或者方法名。這時候你就可以進一步對產生問題的函式的彙編代碼進行分析了。採用IDA工具進行彙編代碼分析的缺點是靜態分析無法看到運行時的各個暫存器的真實的值,因此採用這種方法可能更需要考慮你對彙編代碼的理解能力。下麵就是本文例子中的[UIWebDocumentView _updateSubviewCaches]方法的實現彙編代碼:


IDA工具查看_updateSubviewCaches的實現


採用IDA工具進行分析時,需要瞭解一些比如庫基地址和代碼資料偏移地址以及地址重定向相關的知識。蘋果系統為安全對每個庫的加載都採用了ASLR的方式,也就是庫所加載的基地址每次運行時都是隨機的,這樣當某次崩潰發生時需要將產生崩潰時的地址轉化為我們通過IDA工具打開的地址。 轉換公式為:


   轉換後的地址 = 崩潰時暫存器中儲存的原始地址值 -  崩潰時地址所在的庫的基地址值 + 工具打開庫時所設定的基地址。


就以上面崩潰異常為例,當我們用IDA工具看看x1暫存器中的值到底是一個什麼方法名,那麼只需要把x1的值(0x018eb89b7b),減去其所在的庫UIKit的基地址值(0x18e03d000),在加上IDA工具打開庫時的基地址(要想看基地址則滾動到IDA視圖的最開始部分,本次打開的基地址為:0x187769000)。所以x1暫存器中的地址值被轉化後應該為:


0x018eb89b7b -  0x18e03d000 + 0x187769000 = 0x1882B5B7B


在IDA工具中將地址跳轉到0x1882B5B7B就可以看到本例子中產生崩潰的方法名是叫release:


導致崩潰異常的方法名


當然IDA工具是可以手動進行基地址的自定義設置的,這樣就不需要進行計算以便和線上崩潰的基地址對齊。


如果你手頭上沒有第三方工具,其實系統內置的otools工具也可以幫我們進行問題的定位以及彙編代碼的查看和分析了,具體的方法大家就去查找相關的對otools使用的教程即可,這裡就不展開了。


總結


上面列出的所有分析方法中有靜態分析的也有動態分析。當出現了崩潰時除了從崩潰函式呼叫棧去分析問題,還可以從暫存器,以及加載的鏡像串列,以及崩潰棧頂部的函式的彙編代碼等等進行綜合的分析和判斷。當然即使這樣也不能保證所有問題就一定能夠得到解決,本文中列舉的例子只是在實際中的一種非常常見的崩潰異常,希望通過這個示例來起到一個拋磚引玉的效果,畢竟不同的崩潰異常的差異是比較大的。遇到問題需要具體分析,走進函式的內部實現就一定能夠找到產生問題的根源。


歡迎大家訪問我的github地址:https://github.com/youngsoft

和簡書地址:https://www.jianshu.com/u/3c9287519f58


編號295,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂